started adding binary fbx support

This commit is contained in:
Dmitry
2019-08-24 10:27:58 +03:00
parent 3655f0b202
commit 605c3aa7e4
17 changed files with 1244 additions and 484 deletions
+8 -11
View File
@@ -6,24 +6,21 @@
<component name="ChangeListManager">
<list default="true" id="1932b2c4-d6ff-413c-9558-19959667326b" name="Default Changelist" comment="scene graph">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Cargo.lock" beforeDir="false" afterPath="$PROJECT_DIR$/Cargo.lock" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Cargo.toml" beforeDir="false" afterPath="$PROJECT_DIR$/Cargo.toml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/save.bin" beforeDir="false" afterPath="$PROJECT_DIR$/save.bin" afterDir="false" />
<change beforePath="$PROJECT_DIR$/save.txt" beforeDir="false" afterPath="$PROJECT_DIR$/save.txt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/engine/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/engine/mod.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/game/level.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/game/level.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/game/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/game/mod.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/game/player.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/game/player.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/main.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/math/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/math/mod.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/math/quat.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/math/quat.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/math/vec3.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/math/vec3.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/renderer/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/renderer/mod.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/renderer/gpu_program.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/renderer/gpu_program.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/renderer/render.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/renderer/render.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/renderer/surface.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/renderer/surface.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/resource/fbx/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/resource/fbx/mod.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/resource/model.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/resource/model.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/resource/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/resource/mod.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/scene/animation.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/scene/animation.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/scene/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/scene/mod.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/scene/node.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/scene/node.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/utils/pool.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/utils/pool.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/utils/visitor.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/utils/visitor.rs" afterDir="false" />
</list>
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="SHOW_DIALOG" value="false" />
@@ -77,7 +74,7 @@
</list>
</option>
</component>
<component name="RunManager" selected="Cargo Command.Release">
<component name="RunManager" selected="Cargo Command.Clippy">
<configuration name="Clippy" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
<option name="channel" value="DEFAULT" />
<option name="command" value="clippy" />
Generated
+1
View File
@@ -759,6 +759,7 @@ dependencies = [
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glutin 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
"image 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
"inflate 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"lexical 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
+2 -1
View File
@@ -15,4 +15,5 @@ glutin = "*"
image = "*"
lexical = "*"
byteorder = "*"
base64="*"
base64 = "*"
inflate = "*"
+35 -4
View File
@@ -1,13 +1,44 @@
# What is this?
3d shooter written in Rust. I'm just learning Rust so code may look like a mess.
3d shooter written in Rust. This project ports many features from DmitrysEngine (written in C), it will eventually be split for Engine and Game parts.
# Screenshots
- Soon
# What is done already?
## Engine
- Window (using glutin)
- Object pool
- Scene graph with pivot, camera, mesh nodes.
- FBX Loader
- FBX Loader (ASCII only for now, binary coming soon)
- TTF Loader
- Advanced UI
- Fonts
- Position-based physics
- Object visitor (built-in save/load)
- Automatic resource management
- Texture
- Models
## Game
- Player
- Weapons
- TTF Loader
- Simple level
- Fonts
# What will be added soon?
- Shadows
- Deferred shading
- Particles systems (soft particles)
- GJK-EPA based collision solver
- Sound
- Ray-cast for physics
# Dependencies
glutin - window and OpenGL initialization
image - texture loading
lexical - fast text -> number parsing for ASCII FBX loader
byteorder - read/write builtin rust types (u16, u32, f32, etc.)
base64 - encode binary data for object visitor's text output
Binary file not shown.
BIN
View File
Binary file not shown.
+387 -387
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -9,6 +9,7 @@ extern crate lexical;
extern crate byteorder;
extern crate base64;
extern crate inflate;
mod utils;
mod math;
+5 -5
View File
@@ -165,21 +165,21 @@ pub fn wrapf(mut n: f32, mut min_limit: f32, mut max_limit: f32) -> f32 {
return 0.0;
}
max_limit = max_limit - min_limit;
max_limit -= min_limit;
let offset = min_limit;
min_limit = 0.0;
n = n - offset;
n -= offset;
let num_of_max = (n / max_limit).abs().floor();
if n >= max_limit {
n = n - num_of_max * max_limit;
n -= num_of_max * max_limit;
} else if n < min_limit {
n = ((num_of_max + 1.0) * max_limit) + n;
n += (num_of_max + 1.0) * max_limit;
}
return n + offset;
n + offset
}
pub fn lerpf(a: f32, b: f32, t: f32) -> f32 {
+1 -1
View File
@@ -74,7 +74,7 @@ impl Quat {
pub fn angle(&self, other: &Quat) -> f32 {
let s = (self.sqr_len() * other.sqr_len()).sqrt();
return (self.dot(other) / s).acos();
(self.dot(other) / s).acos()
}
pub fn slerp(&self, other: &Quat, t: f32) -> Quat {
+36 -8
View File
@@ -2,16 +2,23 @@ use crate::{
renderer::{
gl::types::GLuint,
gl,
gl::types::GLint
}
gl::types::GLint,
gl::types::GLfloat,
},
math::mat4::Mat4,
};
use std::ffi::CStr;
pub struct GpuProgram {
pub(in crate::renderer) id: GLuint,
id: GLuint,
name_buf: Vec<u8>,
}
#[derive(Copy, Clone)]
pub struct UniformLocation {
id: GLint
}
impl GpuProgram {
pub fn create_shader(actual_type: GLuint, source: &CStr) -> Result<GLuint, String> {
unsafe {
@@ -25,8 +32,11 @@ impl GpuProgram {
let mut log_len = 0;
gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut log_len);
let mut buffer: Vec<u8> = Vec::with_capacity(log_len as usize);
buffer.set_len(log_len as usize);
gl::GetShaderInfoLog(shader, log_len, std::ptr::null_mut(), buffer.as_mut_ptr() as *mut i8);
Err(String::from_utf8_unchecked(buffer))
let compilation_message = String::from_utf8_unchecked(buffer);
println!("Failed to compile shader: {}", compilation_message);
Err(compilation_message)
} else {
println!("Shader compiled!");
Ok(shader)
@@ -36,8 +46,8 @@ impl GpuProgram {
pub fn from_source(vertex_source: &CStr, fragment_source: &CStr) -> Result<GpuProgram, String> {
unsafe {
let vertex_shader = Self::create_shader(gl::VERTEX_SHADER, vertex_source).unwrap();
let fragment_shader = Self::create_shader(gl::FRAGMENT_SHADER, fragment_source).unwrap();
let vertex_shader = Self::create_shader(gl::VERTEX_SHADER, vertex_source)?;
let fragment_shader = Self::create_shader(gl::FRAGMENT_SHADER, fragment_source)?;
let program: GLuint = gl::CreateProgram();
gl::AttachShader(program, vertex_shader);
gl::DeleteShader(vertex_shader);
@@ -61,14 +71,32 @@ impl GpuProgram {
}
}
pub fn get_uniform_location(&mut self, name: &str) -> GLint {
pub fn get_uniform_location(&mut self, name: &str) -> UniformLocation {
// Form c string in special buffer to reduce memory allocations
let buf = &mut self.name_buf;
buf.clear();
buf.extend_from_slice(name.as_bytes());
buf.push(0);
unsafe {
gl::GetUniformLocation(self.id, buf.as_ptr() as *const i8)
UniformLocation { id: gl::GetUniformLocation(self.id, buf.as_ptr() as *const i8) }
}
}
pub fn bind(&self) {
unsafe {
gl::UseProgram(self.id);
}
}
pub fn set_mat4(&self, location: UniformLocation, mat: &Mat4) {
unsafe {
gl::UniformMatrix4fv(location.id, 1, gl::FALSE, &mat.f as *const GLfloat);
}
}
pub fn set_int(&self, location: UniformLocation, value: i32) {
unsafe {
gl::Uniform1i(location.id, value);
}
}
}
+463 -16
View File
@@ -40,7 +40,10 @@ use crate::{
},
gl,
gl::types::*,
gpu_program::GpuProgram,
gpu_program::{
GpuProgram,
UniformLocation,
},
},
math::{
vec3::Vec3,
@@ -49,8 +52,6 @@ use crate::{
},
};
// Welcome to the kingdom of Unsafe Code
pub fn check_gl_error() {
unsafe {
match gl::GetError() {
@@ -62,14 +63,45 @@ pub fn check_gl_error() {
struct UIShader {
program: GpuProgram,
wvp_matrix: GLint,
diffuse_texture: GLint,
wvp_matrix: UniformLocation,
diffuse_texture: UniformLocation,
}
struct FlatShader {
program: GpuProgram,
wvp_matrix: GLint,
diffuse_texture: GLint,
wvp_matrix: UniformLocation,
diffuse_texture: UniformLocation,
}
struct DeferredLightingShader {
program: GpuProgram,
wvp_matrix: UniformLocation,
depth_sampler: UniformLocation,
color_sampler: UniformLocation,
normal_sampler: UniformLocation,
spot_shadow_texture: UniformLocation,
point_shadow_texture: UniformLocation,
light_view_proj_matrix: UniformLocation,
light_type: UniformLocation,
soft_shadows: UniformLocation,
shadow_map_inv_size: UniformLocation,
light_position: UniformLocation,
light_radius: UniformLocation,
light_color: UniformLocation,
light_direction: UniformLocation,
light_cone_angle_cos: UniformLocation,
inv_view_proj_matrix: UniformLocation,
camera_position: UniformLocation,
}
struct GBufferShader {
program: GpuProgram,
world_matrix: UniformLocation,
wvp_matrix: UniformLocation,
use_skeletal_animation: UniformLocation,
bone_matrices: UniformLocation,
diffuse_texture: UniformLocation,
normal_texture: UniformLocation,
}
struct UIRenderBuffers {
@@ -78,6 +110,19 @@ struct UIRenderBuffers {
ebo: GLuint,
}
struct GBuffer {
fbo: GLuint,
depth_rt: GLuint,
depth_buffer: GLuint,
depth_texture: GLuint,
color_rt: GLuint,
color_texture: GLuint,
normal_rt: GLuint,
normal_texture: GLuint,
opt_fbo: GLuint,
frame_texture: GLuint,
}
pub struct Statistics {
pub frame_time: f32,
pub mean_fps: usize,
@@ -107,6 +152,9 @@ pub struct Renderer {
pub(crate) context: glutin::WindowedContext,
flat_shader: FlatShader,
ui_shader: UIShader,
deferred_light_shader: DeferredLightingShader,
gbuffer_shader: GBufferShader,
gbuffer: GBuffer,
/// Dummy white one pixel texture which will be used as stub when rendering
/// something without texture specified.
white_dummy: GLuint,
@@ -207,6 +255,290 @@ fn create_ui_shader() -> UIShader {
}
}
fn create_g_buffer_shader() -> GBufferShader {
let fragment_source = CString::new(r#"
#version 330 core
layout(location = 0) out float outDepth;
layout(location = 1) out vec4 outColor;
layout(location = 2) out vec4 outNormal;
uniform sampler2D diffuseTexture;
uniform sampler2D normalTexture;
uniform sampler2D specularTexture;
in vec4 position;
in vec3 normal;
in vec2 texCoord;
in vec3 tangent;
in vec3 binormal;
void main()
{
outDepth = position.z / position.w;
outColor = texture2D(diffuseTexture, texCoord);
if(outColor.a < 0.5) discard;
outColor.a = 1;
vec4 n = normalize(texture2D(normalTexture, texCoord) * 2.0 - 1.0);
mat3 tangentSpace = mat3(tangent, binormal, normal);
outNormal.xyz = normalize(tangentSpace * n.xyz) * 0.5 + 0.5;
outNormal.w = texture2D(specularTexture, texCoord).r;
}
"#).unwrap();
let vertex_source = CString::new(r#"
#version 330 core
layout(location = 0) in vec3 vertexPosition;
layout(location = 1) in vec2 vertexTexCoord;
layout(location = 2) in vec3 vertexNormal;
layout(location = 3) in vec4 vertexTangent;
layout(location = 4) in vec4 boneWeights;
layout(location = 5) in vec4 boneIndices;
uniform mat4 worldMatrix;
uniform mat4 worldViewProjection;
uniform bool useSkeletalAnimation;
uniform mat4 boneMatrices[60];
out vec4 position;
out vec3 normal;
out vec2 texCoord;
out vec3 tangent;
out vec3 binormal;
void main()
{
vec4 localPosition = vec4(0);
vec3 localNormal = vec3(0);
vec3 localTangent = vec3(0);
if(useSkeletalAnimation)
{
vec4 vertex = vec4(vertexPosition, 1.0);
int i0 = int(boneIndices.x);
int i1 = int(boneIndices.y);
int i2 = int(boneIndices.z);
int i3 = int(boneIndices.w);
localPosition += boneMatrices[i0] * vertex * boneWeights.x;
localPosition += boneMatrices[i1] * vertex * boneWeights.y;
localPosition += boneMatrices[i2] * vertex * boneWeights.z;
localPosition += boneMatrices[i3] * vertex * boneWeights.w;
localNormal += mat3(boneMatrices[i0]) * vertexNormal * boneWeights.x;
localNormal += mat3(boneMatrices[i1]) * vertexNormal * boneWeights.y;
localNormal += mat3(boneMatrices[i2]) * vertexNormal * boneWeights.z;
localNormal += mat3(boneMatrices[i3]) * vertexNormal * boneWeights.w;
localTangent += mat3(boneMatrices[i0]) * vertexTangent.xyz * boneWeights.x;
localTangent += mat3(boneMatrices[i1]) * vertexTangent.xyz * boneWeights.y;
localTangent += mat3(boneMatrices[i2]) * vertexTangent.xyz * boneWeights.z;
localTangent += mat3(boneMatrices[i3]) * vertexTangent.xyz * boneWeights.w;
}
else
{
localPosition = vec4(vertexPosition, 1.0);
localNormal = vertexNormal;
localTangent = vertexTangent.xyz;
}
gl_Position = worldViewProjection * localPosition;
normal = normalize(mat3(worldMatrix) * localNormal);
tangent = normalize(mat3(worldMatrix) * localTangent);
binormal = normalize(vertexTangent.w * cross(tangent, normal));
texCoord = vertexTexCoord;
position = gl_Position;
}
"#).unwrap();
let mut program = GpuProgram::from_source(&vertex_source, &fragment_source).unwrap();
GBufferShader {
world_matrix: program.get_uniform_location("worldMatrix"),
wvp_matrix: program.get_uniform_location("worldViewProjection"),
use_skeletal_animation: program.get_uniform_location("useSkeletalAnimation"),
bone_matrices: program.get_uniform_location("boneMatrices"),
diffuse_texture: program.get_uniform_location("diffuseTexture"),
normal_texture: program.get_uniform_location("normalTexture"),
program,
}
}
fn create_deferred_lighting_shaders() -> DeferredLightingShader {
let fragment_source = CString::new(r#"
#version 330 core
uniform sampler2D depthTexture;
uniform sampler2D colorTexture;
uniform sampler2D normalTexture;
uniform sampler2D spotShadowTexture;
uniform samplerCube pointShadowTexture;
uniform mat4 lightViewProjMatrix;
uniform vec3 lightPos;
uniform float lightRadius;
uniform vec4 lightColor;
uniform vec3 lightDirection;
uniform float coneAngleCos;
uniform mat4 invViewProj;
uniform vec3 cameraPosition;
uniform int lightType;
uniform bool softShadows;
uniform float shadowMapInvSize;
in vec2 texCoord;
out vec4 FragColor;
vec3 GetProjection(vec3 worldPosition, mat4 viewProjectionMatrix)
{
vec4 projPos = viewProjectionMatrix * vec4(worldPosition, 1);
projPos /= projPos.w;
return vec3(projPos.x * 0.5 + 0.5, projPos.y * 0.5 + 0.5, projPos.z * 0.5 + 0.5);
}
void main()
{
vec4 normalSpecular = texture2D(normalTexture, texCoord);
vec3 normal = normalize(normalSpecular.xyz * 2.0 - 1.0);
vec4 screenPosition;
screenPosition.x = texCoord.x * 2.0 - 1.0;
screenPosition.y = texCoord.y * 2.0 - 1.0;
screenPosition.z = texture2D(depthTexture, texCoord).r;
screenPosition.w = 1.0;
vec4 worldPosition = invViewProj * screenPosition;
worldPosition /= worldPosition.w;
vec3 lightVector = lightPos - worldPosition.xyz;
float distanceToLight = length(lightVector);
float d = min(distanceToLight, lightRadius);
vec3 normLightVector = lightVector / d;
vec3 h = normalize(lightVector + (cameraPosition - worldPosition.xyz));
vec3 specular = normalSpecular.w * vec3(0.4 * pow(clamp(dot(normal, h), 0.0, 1.0), 80));
float y = dot(lightDirection, normLightVector);
float k = max(dot(normal, normLightVector), 0);
float attenuation = 1.0 + cos((d / lightRadius) * 3.14159);
if (y < coneAngleCos)
{
attenuation *= smoothstep(coneAngleCos - 0.1, coneAngleCos, y);
}
float shadow = 1.0;
if (lightType == 2) /* Spot light shadows */
{
vec3 lightSpacePosition = GetProjection(worldPosition.xyz, lightViewProjMatrix);
const float bias = 0.00005;
if (softShadows)
{
for (float y = -1.5; y <= 1.5; y += 0.5)
{
for (float x = -1.5; x <= 1.5; x += 0.5)
{
vec2 fetchTexCoord = lightSpacePosition.xy + vec2(x, y) * shadowMapInvSize;
if (lightSpacePosition.z - bias > texture(spotShadowTexture, fetchTexCoord).r)
{
shadow += 1.0;
}
}
}
shadow = clamp(1.0 - shadow / 9.0, 0.0, 1.0);
}
else
{
if (lightSpacePosition.z - bias > texture(spotShadowTexture, lightSpacePosition.xy).r)
{
shadow = 0.0;
}
}
}
else if(lightType == 0) /* Point light shadows */
{
const float bias = 0.01;
if (softShadows)
{
const int samples = 20;
const vec3 directions[samples] = vec3[samples] (
vec3(1, 1, 1), vec3( 1, -1, 1), vec3(-1, -1, 1), vec3(-1, 1, 1),
vec3(1, 1, -1), vec3( 1, -1, -1), vec3(-1, -1, -1), vec3(-1, 1, -1),
vec3(1, 1, 0), vec3( 1, -1, 0), vec3(-1, -1, 0), vec3(-1, 1, 0),
vec3(1, 0, 1), vec3(-1, 0, 1), vec3( 1, 0, -1), vec3(-1, 0, -1),
vec3(0, 1, 1), vec3( 0, -1, 1), vec3( 0, -1, -1), vec3( 0, 1, -1)
);
const float diskRadius = 0.0025;
for (int i = 0; i < samples; ++i)
{
vec3 fetchDirection = -normLightVector + directions[i] * diskRadius;
float shadowDistanceToLight = texture(pointShadowTexture, fetchDirection).r;
if (distanceToLight - bias > shadowDistanceToLight)
{
shadow += 1.0;
}
}
shadow = clamp(1.0 - shadow / float(samples), 0.0, 1.0);
}
else
{
float shadowDistanceToLight = texture(pointShadowTexture, -normLightVector).r;
if (distanceToLight - bias > shadowDistanceToLight)
{
shadow = 0.0;
}
}
}
FragColor = texture2D(colorTexture, texCoord);
FragColor.xyz += specular;
FragColor *= k * shadow * attenuation * lightColor;
}
"#).unwrap();
let vertex_source = CString::new(r#"
#version 330 core
layout(location = 0) in vec3 vertexPosition;
layout(location = 1) in vec2 vertexTexCoord;
uniform mat4 worldViewProjection;
out vec2 texCoord;
void main()
{
gl_Position = worldViewProjection * vec4(vertexPosition, 1.0);
texCoord = vertexTexCoord;
}
"#).unwrap();
let mut program = GpuProgram::from_source(&vertex_source, &fragment_source).unwrap();
DeferredLightingShader {
wvp_matrix: program.get_uniform_location("worldViewProjection"),
depth_sampler: program.get_uniform_location("depthTexture"),
color_sampler: program.get_uniform_location("colorTexture"),
normal_sampler: program.get_uniform_location("normalTexture"),
spot_shadow_texture: program.get_uniform_location("spotShadowTexture"),
point_shadow_texture: program.get_uniform_location("pointShadowTexture"),
light_view_proj_matrix: program.get_uniform_location("lightViewProjMatrix"),
light_type: program.get_uniform_location("lightType"),
soft_shadows: program.get_uniform_location("softShadows"),
shadow_map_inv_size: program.get_uniform_location("shadowMapInvSize"),
light_position: program.get_uniform_location("lightPos"),
light_radius: program.get_uniform_location("lightRadius"),
light_color: program.get_uniform_location("lightColor"),
light_direction: program.get_uniform_location("lightDirection"),
light_cone_angle_cos: program.get_uniform_location("coneAngleCos"),
inv_view_proj_matrix: program.get_uniform_location("invViewProj"),
camera_position: program.get_uniform_location("cameraPosition"),
program,
}
}
fn create_ui_render_buffers() -> UIRenderBuffers {
unsafe {
let mut ui_render_buffers = UIRenderBuffers {
@@ -223,6 +555,118 @@ fn create_ui_render_buffers() -> UIRenderBuffers {
}
}
fn create_gbuffer(width: i32, height: i32) -> GBuffer
{
unsafe {
let mut fbo = 0;
gl::GenFramebuffers(1, &mut fbo);
gl::BindFramebuffer(gl::FRAMEBUFFER, fbo);
let buffers = [
gl::COLOR_ATTACHMENT0,
gl::COLOR_ATTACHMENT1,
gl::COLOR_ATTACHMENT2
];
gl::DrawBuffers(3, buffers.as_ptr());
let mut depth_rt = 0;
gl::GenRenderbuffers(1, &mut depth_rt);
gl::BindRenderbuffer(gl::RENDERBUFFER, depth_rt);
gl::RenderbufferStorage(gl::RENDERBUFFER, gl::R32F, width, height);
gl::FramebufferRenderbuffer(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::RENDERBUFFER, depth_rt);
let mut color_rt = 0;
gl::GenRenderbuffers(1, &mut color_rt);
gl::BindRenderbuffer(gl::RENDERBUFFER, color_rt);
gl::RenderbufferStorage(gl::RENDERBUFFER, gl::RGBA8, width, height);
gl::FramebufferRenderbuffer(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT1, gl::RENDERBUFFER, color_rt);
let mut normal_rt= 0;
gl::GenRenderbuffers(1, &mut normal_rt);
gl::BindRenderbuffer(gl::RENDERBUFFER, normal_rt);
gl::RenderbufferStorage(gl::RENDERBUFFER, gl::RGBA8, width, height);
gl::FramebufferRenderbuffer(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT2, gl::RENDERBUFFER, normal_rt);
let mut depth_buffer = 0;
gl::GenRenderbuffers(1, &mut depth_buffer);
gl::BindRenderbuffer(gl::RENDERBUFFER, depth_buffer);
gl::RenderbufferStorage(gl::RENDERBUFFER, gl::DEPTH24_STENCIL8, width, height);
gl::FramebufferRenderbuffer(gl::FRAMEBUFFER, gl::DEPTH_STENCIL_ATTACHMENT, gl::RENDERBUFFER, depth_buffer);
let mut depth_texture = 0;
gl::GenTextures(1, &mut depth_texture);
gl::BindTexture(gl::TEXTURE_2D, depth_texture);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as i32);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as i32);
gl::TexImage2D(gl::TEXTURE_2D, 0, gl::R32F as i32, width, height, 0, gl::BGRA, gl::FLOAT, std::ptr::null());
gl::FramebufferTexture2D(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D, depth_texture, 0);
let mut color_texture = 0;
gl::GenTextures(1, &mut color_texture);
gl::BindTexture(gl::TEXTURE_2D, color_texture);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as i32);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as i32);
gl::TexImage2D(gl::TEXTURE_2D, 0, gl::RGBA8 as i32, width, height, 0, gl::BGRA, gl::UNSIGNED_BYTE, std::ptr::null());
gl::FramebufferTexture2D(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT1, gl::TEXTURE_2D, color_texture, 0);
let mut normal_texture = 0;
gl::GenTextures(1, &mut normal_texture);
gl::BindTexture(gl::TEXTURE_2D, normal_texture);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as i32);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as i32);
gl::TexImage2D(gl::TEXTURE_2D, 0, gl::RGBA8 as i32, width, height, 0, gl::BGRA, gl::UNSIGNED_BYTE, std::ptr::null());
gl::FramebufferTexture2D(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT2, gl::TEXTURE_2D, normal_texture, 0);
if gl::CheckFramebufferStatus(gl::FRAMEBUFFER) != gl::FRAMEBUFFER_COMPLETE {
panic!("Unable to construct G-Buffer FBO.");
}
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
/* Create another framebuffer for stencil optimizations */
let mut opt_fbo = 0;
gl::GenFramebuffers(1, &mut opt_fbo);
gl::BindFramebuffer(gl::FRAMEBUFFER, opt_fbo);
let light_buffers = [gl::COLOR_ATTACHMENT0];
gl::DrawBuffers(1, light_buffers.as_ptr());
let mut frame_texture = 0;
gl::GenTextures(1, &mut frame_texture);
gl::BindTexture(gl::TEXTURE_2D, frame_texture);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as i32);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as i32);
gl::TexImage2D(gl::TEXTURE_2D, 0, gl::RGBA8 as i32, width, height, 0, gl::BGRA, gl::UNSIGNED_BYTE, std::ptr::null());
gl::FramebufferTexture2D(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D, frame_texture, 0);
gl::FramebufferRenderbuffer(gl::FRAMEBUFFER, gl::DEPTH_STENCIL_ATTACHMENT, gl::RENDERBUFFER, depth_buffer);
if gl::CheckFramebufferStatus(gl::FRAMEBUFFER) != gl::FRAMEBUFFER_COMPLETE {
panic!("Unable to initialize Stencil FBO.");
}
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
GBuffer {
fbo,
depth_rt,
depth_buffer,
depth_texture,
color_rt,
color_texture,
normal_rt,
normal_texture,
opt_fbo,
frame_texture
}
}
}
fn create_white_dummy() -> GLuint {
unsafe {
let mut texture: GLuint = 0;
@@ -263,8 +707,8 @@ impl Renderer {
let primary_monitor = events_loop.get_primary_monitor();
let mut monitor_dimensions = primary_monitor.get_dimensions();
monitor_dimensions.height *= 0.6;
monitor_dimensions.width *= 0.6;
monitor_dimensions.height *= 0.7;
monitor_dimensions.width *= 0.7;
let window_size = monitor_dimensions.to_logical(primary_monitor.get_hidpi_factor());
let window_builder = glutin::WindowBuilder::new()
@@ -288,6 +732,9 @@ impl Renderer {
events_loop,
flat_shader: create_flat_shader(),
ui_shader: create_ui_shader(),
deferred_light_shader: create_deferred_lighting_shaders(),
gbuffer_shader: create_g_buffer_shader(),
gbuffer: create_gbuffer(window_size.width as i32, window_size.height as i32),
traversal_stack: Vec::new(),
cameras: Vec::new(),
lights: Vec::new(),
@@ -305,7 +752,7 @@ impl Renderer {
fn draw_surface(&self, surf: &Surface) {
unsafe {
if let Some(resource) = surf.get_texture() {
if let Some(resource) = surf.get_diffuse_texture() {
if let ResourceKind::Texture(texture) = resource.borrow().borrow_kind() {
gl::BindTexture(gl::TEXTURE_2D, texture.gpu_tex);
} else {
@@ -468,7 +915,7 @@ impl Renderer {
gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
gl::Disable(gl::CULL_FACE);
gl::UseProgram(self.ui_shader.program.id);
self.ui_shader.program.bind();
gl::ActiveTexture(gl::TEXTURE0);
let index_bytes = drawing_context.get_indices_bytes();
@@ -507,7 +954,7 @@ impl Renderer {
0.0,
-1.0,
1.0);
gl::UniformMatrix4fv(self.ui_shader.wvp_matrix, 1, gl::FALSE, ortho.f.as_ptr() as *const f32);
self.ui_shader.program.set_mat4(self.ui_shader.wvp_matrix, &ortho);
for cmd in drawing_context.get_commands() {
let index_count = cmd.get_triangle_count() * 3;
@@ -535,7 +982,7 @@ impl Renderer {
if cmd.get_texture() != 0 {
gl::ActiveTexture(gl::TEXTURE0);
gl::Uniform1i(self.ui_shader.diffuse_texture, 0);
self.ui_shader.program.set_int(self.ui_shader.diffuse_texture, 0);
gl::BindTexture(gl::TEXTURE_2D, cmd.get_texture());
} else {
gl::BindTexture(gl::TEXTURE_2D, self.white_dummy);
@@ -591,7 +1038,7 @@ impl Renderer {
}
}
gl::UseProgram(self.flat_shader.program.id);
self.flat_shader.program.bind();
// Render scene from each camera
for camera_handle in self.cameras.iter() {
@@ -615,8 +1062,8 @@ impl Renderer {
let mvp = view_projection * *node.get_global_transform();
gl::UseProgram(self.flat_shader.program.id);
gl::UniformMatrix4fv(self.flat_shader.wvp_matrix, 1, gl::FALSE, &mvp.f as *const GLfloat);
self.flat_shader.program.bind();
self.flat_shader.program.set_mat4(self.flat_shader.wvp_matrix, &mvp);
if let NodeKind::Mesh(mesh) = node.borrow_kind() {
for surface in mesh.get_surfaces().iter() {
+25 -9
View File
@@ -1,5 +1,9 @@
use crate::{
math::{vec2::*, vec3::*, vec4::*},
math::{
vec2::Vec2,
vec3::Vec3,
vec4::Vec4,
},
renderer::{
gl,
gl::types::*,
@@ -413,8 +417,10 @@ impl Drop for SurfaceSharedData {
}
pub struct Surface {
pub data: Rc<RefCell<SurfaceSharedData>>,
pub texture: Option<Rc<RefCell<Resource>>>,
data: Rc<RefCell<SurfaceSharedData>>,
diffuse_texture: Option<Rc<RefCell<Resource>>>,
normal_texture: Option<Rc<RefCell<Resource>>>,
specular_texture: Option<Rc<RefCell<Resource>>>
}
impl Surface {
@@ -422,7 +428,9 @@ impl Surface {
pub fn new(data: Rc<RefCell<SurfaceSharedData>>) -> Self {
Self {
data,
texture: None,
diffuse_texture: None,
normal_texture: None,
specular_texture: None,
}
}
@@ -432,23 +440,31 @@ impl Surface {
}
#[inline]
pub fn get_texture(&self) -> Option<Rc<RefCell<Resource>>> {
match &self.texture {
pub fn get_diffuse_texture(&self) -> Option<Rc<RefCell<Resource>>> {
match &self.diffuse_texture {
Some(resource) => Some(Rc::clone(resource)),
None => None
}
}
#[inline]
pub fn set_texture(&mut self, tex: Rc<RefCell<Resource>>) {
self.texture = Some(tex);
pub fn set_diffuse_texture(&mut self, tex: Rc<RefCell<Resource>>) {
self.diffuse_texture = Some(tex);
}
#[inline]
pub fn make_copy(&self) -> Surface {
Surface {
data: Rc::clone(&self.data),
texture: match &self.texture {
diffuse_texture: match &self.diffuse_texture {
Some(resource) => Some(Rc::clone(resource)),
None => None
},
normal_texture: match &self.normal_texture {
Some(resource) => Some(Rc::clone(resource)),
None => None
},
specular_texture: match &self.specular_texture {
Some(resource) => Some(Rc::clone(resource)),
None => None
},
+242 -12
View File
@@ -1,15 +1,29 @@
use std::{
path::{PathBuf, Path},
path::{
PathBuf,
Path,
},
fs::File,
io::Read,
io::{
Read,
Cursor,
Seek,
SeekFrom
},
collections::HashMap,
time::Instant,
any::{Any, TypeId},
any::{
Any,
TypeId,
},
cell::RefCell,
rc::Rc,
};
use crate::{
utils::pool::*,
utils::pool::{
Handle,
Pool,
},
math::{
vec4::Vec4,
vec3::Vec3,
@@ -21,7 +35,15 @@ use crate::{
},
triangulator::triangulate,
},
scene::{*, node::*},
scene::{
*,
node::*,
animation::{
Track,
KeyFrame,
Animation,
},
},
renderer::surface::{
SurfaceSharedData,
Surface,
@@ -29,9 +51,10 @@ use crate::{
},
engine::State,
};
use crate::scene::animation::{Track, KeyFrame, Animation};
// TODO: Add binary FBX support
use byteorder::{
ReadBytesExt,
LittleEndian
};
pub enum FbxAttribute {
Double(f64),
@@ -292,7 +315,7 @@ impl FbxAnimationCurve {
// Must be unreached
println!("FBX: How the hell did you get here?!");
return 0.0;
0.0
}
}
@@ -387,6 +410,17 @@ struct FbxNode {
children: Vec<Handle<FbxNode>>,
}
impl Default for FbxNode {
fn default() -> Self {
Self {
name: "".to_string(),
attribs: Vec::new(),
parent: Default::default(),
children: Vec::new(),
}
}
}
impl FbxNode {
fn get_vec3_at(&self, n: usize) -> Result<Vec3, String> {
Ok(Vec3 {
@@ -912,6 +946,200 @@ fn read_ascii(path: &Path) -> Result<Fbx, String> {
})
}
pub enum FbxError {
Io(std::io::Error),
UnknownAttributeType(u8),
InvalidNullRecord,
InvalidString,
Custom(String),
UnsupportedVersion(u32),
}
impl From<std::io::Error> for FbxError {
fn from(err: std::io::Error) -> Self {
FbxError::Io(err)
}
}
impl From<String> for FbxError {
fn from(err: String) -> Self {
FbxError::Custom(err)
}
}
impl From<std::string::FromUtf8Error> for FbxError {
fn from(_: std::string::FromUtf8Error) -> Self {
FbxError::InvalidString
}
}
fn read_attrib<R>(type_code: u8, file: &mut R) -> Result<FbxAttribute, FbxError>
where R: Read {
match type_code {
b'f' | b'F' => Ok(FbxAttribute::Float(file.read_f32::<LittleEndian>()?)),
b'd' | b'D' => Ok(FbxAttribute::Double(file.read_f64::<LittleEndian>()?)),
b'l' | b'L' => Ok(FbxAttribute::Long(file.read_i64::<LittleEndian>()?)),
b'i' | b'I' => Ok(FbxAttribute::Integer(file.read_i32::<LittleEndian>()?)),
b'Y' => Ok(FbxAttribute::Integer(i32::from(file.read_i16::<LittleEndian>()?))),
b'b' | b'C' => Ok(FbxAttribute::Bool(file.read_u8()? != 0)),
_ => Err(FbxError::UnknownAttributeType(type_code))
}
}
fn read_array(type_code: u8, file: &mut File) -> Result<Vec<FbxAttribute>, FbxError> {
let length = file.read_u32::<LittleEndian>()? as usize;
let encoding = file.read_u32::<LittleEndian>()?;
let compressed_length = file.read_u32::<LittleEndian>()? as usize;
let mut array = Vec::new();
if encoding == 0 {
for _ in 0..length {
array.push(read_attrib(type_code, file)?);
}
} else {
let mut compressed = Vec::with_capacity(compressed_length);
unsafe { compressed.set_len(compressed_length) };
file.read_exact(compressed.as_mut_slice())?;
let decompressed = inflate::inflate_bytes(&compressed)?;
let mut cursor = Cursor::new(decompressed);
for _ in 0..length {
array.push(read_attrib(type_code, &mut cursor)?);
}
}
Ok(array)
}
fn read_string(file: &mut File) -> Result<FbxAttribute, FbxError> {
let length = file.read_u32::<LittleEndian>()? as usize;
let mut raw_string = Vec::with_capacity(length);
unsafe { raw_string.set_len(length); };
file.read_exact(raw_string.as_mut_slice())?;
Ok(FbxAttribute::String(String::from_utf8(raw_string)?))
}
/// Read binary FBX DOM using this specification:
/// https://code.blender.org/2013/08/fbx-binary-file-format-specification/
/// In case of success returns Ok(valid_handle), in case if no more nodes
/// are present returns Ok(none_handle), in case of error returns some FbxError.
fn read_binary_node(file: &mut File, pool: &mut Pool<FbxNode>) -> Result<Handle<FbxNode>, FbxError> {
let end_offset = u64::from(file.read_u32::<LittleEndian>()?);
if end_offset == 0 {
// Footer found. We're done.
return Ok(Handle::none());
}
let num_attrib = file.read_u32::<LittleEndian>()? as usize;
let _attrib_list_len = file.read_u32::<LittleEndian>()?;
// Read name.
let name_len = file.read_u8()? as usize;
let mut raw_name = Vec::with_capacity(name_len);
unsafe { raw_name.set_len(name_len) };
file.read_exact(raw_name.as_mut_slice())?;
let mut node = FbxNode::default();
node.name = String::from_utf8(raw_name)?;
let node_handle = pool.spawn(node);
// Read attributes.
for _ in 0..num_attrib {
let type_code = file.read_u8()?;
match type_code {
b'C' | b'Y' | b'I' | b'F' | b'D' | b'L' => {
if let Some(node) = pool.borrow_mut(&node_handle) {
node.attribs.push(read_attrib(type_code, file)?)
}
}
b'f' | b'd' | b'l' | b'i' | b'b' => {
let mut a = FbxNode::default();
a.name = String::from("a");
a.attribs = read_array(type_code, file)?;
a.parent = node_handle.clone();
let a_handle = pool.spawn(a);
if let Some(node) = pool.borrow_mut(&node_handle) {
node.children.push(a_handle);
}
}
b'S' => if let Some(node) = pool.borrow_mut(&node_handle) {
node.attribs.push(read_string(file)?)
},
b'R' => (), // Ignore Raw data
_ => ()
}
}
if file.seek(SeekFrom::Current(0))? < end_offset {
let null_record_position = end_offset - 13;
while file.seek(SeekFrom::Current(0))? < null_record_position {
let child_handle = read_binary_node(file, pool)?;
if child_handle.is_none() {
return Ok(child_handle);
}
if let Some(child) = pool.borrow_mut(&child_handle) {
child.parent = node_handle.clone();
}
if let Some(node) = pool.borrow_mut(&node_handle) {
node.children.push(child_handle.clone());
}
}
// Check if we have a null-record
let mut null_record = [0; 13];
file.read_exact(&mut null_record)?;
for i in &null_record {
if *i != 0 {
return Err(FbxError::InvalidNullRecord);
}
}
}
Ok(node_handle)
}
fn read_binary(file: &mut File) -> Result<Fbx, FbxError> {
let total_length = file.seek(SeekFrom::End(0))?;
file.seek(SeekFrom::Start(0))?;
// Ignore all stuff until version.
let mut temp = [0; 23];
file.read_exact(&mut temp)?;
// Verify version.
let version = file.read_u32::<LittleEndian>()?;
if version < 7100 || version > 7400 {
return Err(FbxError::UnsupportedVersion(version));
}
let mut nodes = Pool::new();
let mut root = FbxNode::default();
root.name = String::from("__ROOT__");
let root_handle = nodes.spawn(root);
// FBX document can have multiple root nodes, so we must read the file
// until the end.
while file.seek(SeekFrom::Current(0))? < total_length {
let root_child = read_binary_node(file, &mut nodes)?;
if root_child.is_none() {
break;
}
if let Some(child) = nodes.borrow_mut(&root_child) {
child.parent = root_handle.clone();
}
if let Some(root) = nodes.borrow_mut(&root_handle) {
root.children.push(root_child.clone());
}
}
Ok(Fbx {
nodes,
root: root_handle,
index_to_component: Default::default(),
component_pool: Pool::new(),
components: Vec::new(),
})
}
fn string_to_mapping(value: &str) -> FbxMapping {
match value {
"ByPolygon" => FbxMapping::ByPolygon,
@@ -1158,9 +1386,11 @@ impl Fbx {
if let Some(texture_handle) = self.component_pool.borrow(&material.diffuse_texture) {
if let FbxComponent::Texture(texture) = texture_handle {
if texture.filename.is_relative() {
let fullpath = state.get_resource_manager_mut().get_textures_path().join(&texture.filename);
if let Some(texture_resource) = state.request_resource(fullpath.as_path()) {
surface.set_texture(texture_resource);
//let file_name = texture.filename.file_name();
//let extension = texture.filename.extension();
let diffuse_path = state.get_resource_manager_mut().get_textures_path().join(&texture.filename);
if let Some(texture_resource) = state.request_resource(diffuse_path.as_path()) {
surface.set_diffuse_texture(texture_resource);
}
}
}
+13 -6
View File
@@ -5,15 +5,22 @@ pub mod ttf;
use std::{
path::*,
any::{TypeId, Any}
any::{
TypeId,
Any,
},
};
use crate::{
resource::{
texture::*,
model::{Model}
}
model::Model,
},
utils::visitor::{
Visit,
Visitor,
VisitResult,
},
};
use crate::utils::visitor::{Visit, Visitor, VisitResult};
pub enum ResourceKind {
Unknown,
@@ -23,7 +30,7 @@ pub enum ResourceKind {
pub struct Resource {
path: PathBuf,
kind: ResourceKind
kind: ResourceKind,
}
impl Resource {
@@ -72,7 +79,7 @@ impl Visit for Resource {
visitor.enter_region(name)?;
let mut kind: u8 = if visitor.is_reading() {
0
0
} else {
match &mut self.kind {
ResourceKind::Unknown => panic!("must not get here"),
+23 -22
View File
@@ -2,22 +2,25 @@ use crate::{
math::{
vec3::Vec3,
quat::Quat,
clampf,
lerpf,
wrapf,
},
resource::Resource,
utils::visitor::{
Visit,
VisitResult,
Visitor,
utils::{
visitor::{
Visit,
VisitResult,
Visitor,
},
pool::Handle,
},
scene::node::Node,
utils::pool::Handle,
math::clampf,
};
use std::{
rc::Rc,
cell::RefCell,
};
use crate::math::{lerpf, wrapf};
#[derive(Copy, Clone)]
pub struct KeyFrame {
@@ -33,7 +36,7 @@ impl KeyFrame {
time,
position,
scale,
rotation
rotation,
}
}
}
@@ -157,18 +160,16 @@ impl Track {
if right_index == 0 {
return Some(*self.frames.first().unwrap());
} else {
if let Some(left) = self.frames.get(right_index - 1) {
if let Some(right) = self.frames.get(right_index) {
let interpolator = (time - left.time) / (right.time - left.time);
} else if let Some(left) = self.frames.get(right_index - 1) {
if let Some(right) = self.frames.get(right_index) {
let interpolator = (time - left.time) / (right.time - left.time);
return Some(KeyFrame {
time: lerpf(left.time, right.time, interpolator),
position: left.position.lerp(&right.position, interpolator),
scale: left.scale.lerp(&right.scale, interpolator),
rotation: left.rotation.slerp(&right.rotation, interpolator),
});
}
return Some(KeyFrame {
time: lerpf(left.time, right.time, interpolator),
position: left.position.lerp(&right.position, interpolator),
scale: left.scale.lerp(&right.scale, interpolator),
rotation: left.rotation.slerp(&right.rotation, interpolator),
});
}
}
@@ -199,7 +200,7 @@ impl Clone for Animation {
fade_step: self.fade_step,
looped: self.looped,
enabled: self.enabled,
resource: self.resource.clone()
resource: self.resource.clone(),
}
}
}
@@ -238,7 +239,7 @@ impl Animation {
pub fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
}
pub fn is_enabled(&self) -> bool {
self.enabled
}
@@ -250,7 +251,7 @@ impl Animation {
pub fn get_tracks_mut(&mut self) -> &mut [Track] {
&mut self.tracks
}
pub fn update_fading(&mut self, dt: f32) {
if self.fade_step != 0.0 {
self.weight += self.fade_step * dt;
+2 -2
View File
@@ -909,8 +909,8 @@ mod test {
if visitor.reading {} else {
let mut kind_id: u8 = match &self.kind {
ResourceKind::Unknown => return Err(VisitError::User(format!("Invalid resource!"))),
ResourceKind::Model(model) => 0,
ResourceKind::Texture(texture) => 1
ResourceKind::Model(_) => 0,
ResourceKind::Texture(_) => 1
};
kind_id.visit("KindId", visitor)?;
self.kind.visit("KindData", visitor)?;