mirror of
https://github.com/FyroxEngine/Fyrox.git
synced 2026-10-12 02:02:20 +00:00
started adding binary fbx support
This commit is contained in:
Generated
+8
-11
@@ -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
@@ -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
@@ -15,4 +15,5 @@ glutin = "*"
|
||||
image = "*"
|
||||
lexical = "*"
|
||||
byteorder = "*"
|
||||
base64="*"
|
||||
base64 = "*"
|
||||
inflate = "*"
|
||||
@@ -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.
@@ -9,6 +9,7 @@ extern crate lexical;
|
||||
|
||||
extern crate byteorder;
|
||||
extern crate base64;
|
||||
extern crate inflate;
|
||||
|
||||
mod utils;
|
||||
mod math;
|
||||
|
||||
+5
-5
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
Reference in New Issue
Block a user