render: process GltfScenes and Node local transforms
This commit is contained in:
parent
c1b5ca768f
commit
fba925512b
|
@ -1845,7 +1845,9 @@ dependencies = [
|
||||||
"gltf",
|
"gltf",
|
||||||
"image",
|
"image",
|
||||||
"infer",
|
"infer",
|
||||||
|
"instant",
|
||||||
"lyra-ecs",
|
"lyra-ecs",
|
||||||
|
"lyra-math",
|
||||||
"mime",
|
"mime",
|
||||||
"notify",
|
"notify",
|
||||||
"notify-debouncer-full",
|
"notify-debouncer-full",
|
||||||
|
@ -3101,7 +3103,6 @@ dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"fps_counter",
|
"fps_counter",
|
||||||
"lyra-engine",
|
"lyra-engine",
|
||||||
"lyra-scripting",
|
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,9 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
lyra-engine = { path = "../../", version = "0.0.1", features = ["lua_scripting"] }
|
lyra-engine = { path = "../../", version = "0.0.1" }
|
||||||
lyra-scripting = { path = "../../lyra-scripting", features = ["lua", "teal"] }
|
#lyra-engine = { path = "../../", version = "0.0.1", features = ["lua_scripting"] }
|
||||||
|
#lyra-scripting = { path = "../../lyra-scripting", features = ["lua", "teal"] }
|
||||||
#lyra-ecs = { path = "../../lyra-ecs"}
|
#lyra-ecs = { path = "../../lyra-ecs"}
|
||||||
anyhow = "1.0.75"
|
anyhow = "1.0.75"
|
||||||
async-std = "1.12.0"
|
async-std = "1.12.0"
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,2 @@
|
||||||
|
Sponza*
|
||||||
|
Textures
|
|
@ -0,0 +1 @@
|
||||||
|
To keep the size of this repository down, the Sponza scene is omitted from this repo. The files were downloaded from https://github.com/toji/sponza-optimized
|
|
@ -1,7 +1,7 @@
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
use lyra_engine::{math::{self, Vec3}, math::Transform, input::{KeyCode, ActionHandler, Action, ActionKind, LayoutId, ActionMapping, ActionSource, ActionMappingId, InputActionPlugin, MouseInput, MouseAxis, CommonActionLabel}, game::Game, render::{window::{CursorGrabMode, WindowOptions}, light::{PointLight, directional::DirectionalLight, SpotLight}}, change_tracker::Ct, ecs::{system::{Criteria, CriteriaSchedule, BatchedSystem, IntoSystem}, World, Component}, DeltaTime, scene::{ModelComponent, CameraComponent}, lua::{LuaScriptingPlugin, LuaScript}, Script, ScriptList};
|
use lyra_engine::{assets::gltf::Gltf, ecs::{system::{BatchedSystem, Criteria, CriteriaSchedule, IntoSystem}, Component, World}, game::Game, input::{Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource, InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput}, math::{self, Transform, Vec3}, render::light::{directional::DirectionalLight, PointLight, SpotLight}, scene::{CameraComponent, MeshComponent}, DeltaTime};
|
||||||
use lyra_engine::assets::{ResourceManager, Model};
|
use lyra_engine::assets::ResourceManager;
|
||||||
|
|
||||||
mod free_fly_camera;
|
mod free_fly_camera;
|
||||||
use free_fly_camera::{FreeFlyCameraPlugin, FreeFlyCamera};
|
use free_fly_camera::{FreeFlyCameraPlugin, FreeFlyCamera};
|
||||||
|
@ -16,6 +16,13 @@ pub enum ActionLabel {
|
||||||
LookRoll,
|
LookRoll,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ACTLBL_MOVE_UP_DOWN: &str = "MoveUpDown";
|
||||||
|
const ACTLBL_MOVE_LEFT_RIGHT: &str = "MoveLeftRight";
|
||||||
|
const ACTLBL_MOVE_FORWARD_BACKWARD: &str = "MoveForwardBackward";
|
||||||
|
const ACTLBL_LOOK_LEFT_RIGHT: &str = "LookLeftRight";
|
||||||
|
const ACTLBL_LOOK_UP_DOWN: &str = "LookUpDown";
|
||||||
|
const ACTLBL_LOOK_ROLL: &str = "LookRoll";
|
||||||
|
|
||||||
struct FixedTimestep {
|
struct FixedTimestep {
|
||||||
max_tps: u32,
|
max_tps: u32,
|
||||||
fixed_time: f32,
|
fixed_time: f32,
|
||||||
|
@ -86,30 +93,45 @@ async fn main() {
|
||||||
//let diffuse_texture = resman.request::<Texture>("assets/happy-tree.png").unwrap();
|
//let diffuse_texture = resman.request::<Texture>("assets/happy-tree.png").unwrap();
|
||||||
//let antique_camera_model = resman.request::<Model>("assets/AntiqueCamera.glb").unwrap();
|
//let antique_camera_model = resman.request::<Model>("assets/AntiqueCamera.glb").unwrap();
|
||||||
//let cube_model = resman.request::<Model>("assets/cube-texture-bin.glb").unwrap();
|
//let cube_model = resman.request::<Model>("assets/cube-texture-bin.glb").unwrap();
|
||||||
let cube_model = resman.request::<Model>("assets/texture-sep/texture-sep.gltf").unwrap();
|
let cube_gltf = resman.request::<Gltf>("assets/texture-sep/texture-sep.gltf").unwrap();
|
||||||
let crate_model = resman.request::<Model>("assets/crate/crate.gltf").unwrap();
|
let crate_gltf = resman.request::<Gltf>("assets/crate/crate.gltf").unwrap();
|
||||||
//let sponza_model = resman.request::<Model>("assets/sponza/Sponza.gltf").unwrap();
|
|
||||||
|
let separate_gltf = resman.request::<Gltf>("assets/pos-testing/separate-nodes-two-cubes.glb").unwrap();
|
||||||
|
drop(resman);
|
||||||
|
|
||||||
|
let cube_mesh = &cube_gltf.data_ref()
|
||||||
|
.unwrap().meshes[0];
|
||||||
|
let crate_mesh = &crate_gltf.data_ref()
|
||||||
|
.unwrap().meshes[0];
|
||||||
|
|
||||||
|
let separate_scene = &separate_gltf.data_ref()
|
||||||
|
.unwrap().scenes[0];
|
||||||
|
|
||||||
|
/* let sponza_model = resman.request::<Gltf>("assets/sponza/Sponza.gltf").unwrap();
|
||||||
drop(resman);
|
drop(resman);
|
||||||
|
|
||||||
/* world.spawn((
|
let sponza_scene = &sponza_model.data_ref()
|
||||||
ModelComponent(antique_camera_model),
|
.unwrap().scenes[0];
|
||||||
Transform::from_xyz(0.0, -5.0, -10.0),
|
|
||||||
)); */
|
|
||||||
|
|
||||||
/* world.spawn((
|
world.spawn((
|
||||||
ModelComponent(sponza_model),
|
sponza_scene.clone(),
|
||||||
Transform::from_xyz(0.0, 0.0, 0.0),
|
Transform::from_xyz(0.0, 0.0, 0.0),
|
||||||
)); */
|
)); */
|
||||||
|
|
||||||
{
|
world.spawn((
|
||||||
let cube_tran = Transform::from_xyz(-3.5, 0.0, -8.0);
|
separate_scene.clone(),
|
||||||
|
Transform::from_xyz(0.0, -5.0, -10.0),
|
||||||
|
));
|
||||||
|
|
||||||
|
/* {
|
||||||
|
let cube_tran = Transform::from_xyz(-5.9026427, -1.8953488, -10.0);
|
||||||
//cube_tran.rotate_y(math::Angle::Degrees(180.0));
|
//cube_tran.rotate_y(math::Angle::Degrees(180.0));
|
||||||
world.spawn((
|
world.spawn((
|
||||||
cube_tran,
|
cube_tran,
|
||||||
ModelComponent(crate_model.clone()),
|
crate_mesh.clone(),
|
||||||
CubeFlag,
|
CubeFlag,
|
||||||
));
|
));
|
||||||
}
|
} */
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut light_tran = Transform::from_xyz(1.5, 2.5, 0.0);
|
let mut light_tran = Transform::from_xyz(1.5, 2.5, 0.0);
|
||||||
|
@ -124,11 +146,11 @@ async fn main() {
|
||||||
specular: 1.3,
|
specular: 1.3,
|
||||||
},
|
},
|
||||||
light_tran,
|
light_tran,
|
||||||
ModelComponent(cube_model.clone()),
|
cube_mesh.clone(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
/* {
|
||||||
let mut light_tran = Transform::from_xyz(-3.5, 0.2, -4.5);
|
let mut light_tran = Transform::from_xyz(-3.5, 0.2, -4.5);
|
||||||
light_tran.scale = Vec3::new(0.5, 0.5, 0.5);
|
light_tran.scale = Vec3::new(0.5, 0.5, 0.5);
|
||||||
world.spawn((
|
world.spawn((
|
||||||
|
@ -146,9 +168,31 @@ async fn main() {
|
||||||
specular: 1.0,
|
specular: 1.0,
|
||||||
},
|
},
|
||||||
Transform::from(light_tran),
|
Transform::from(light_tran),
|
||||||
ModelComponent(cube_model.clone()),
|
cube_mesh.clone(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut light_tran = Transform::from_xyz(2.0, 2.5, -9.5);
|
||||||
|
light_tran.scale = Vec3::new(0.5, 0.5, 0.5);
|
||||||
|
world.spawn((
|
||||||
|
PointLight {
|
||||||
|
color: Vec3::new(0.0, 0.0, 1.0),
|
||||||
|
|
||||||
|
intensity: 3.3,
|
||||||
|
|
||||||
|
constant: 1.0,
|
||||||
|
linear: 0.09,
|
||||||
|
quadratic: 0.032,
|
||||||
|
|
||||||
|
ambient: 0.2,
|
||||||
|
diffuse: 1.0,
|
||||||
|
specular: 1.3,
|
||||||
|
},
|
||||||
|
Transform::from(light_tran),
|
||||||
|
cube_mesh.clone(),
|
||||||
|
));
|
||||||
|
} */
|
||||||
|
|
||||||
/* {
|
/* {
|
||||||
let mut light_tran = Transform::from_xyz(-5.0, 2.5, -9.5);
|
let mut light_tran = Transform::from_xyz(-5.0, 2.5, -9.5);
|
||||||
|
@ -172,28 +216,6 @@ async fn main() {
|
||||||
));
|
));
|
||||||
} */
|
} */
|
||||||
|
|
||||||
{
|
|
||||||
let mut light_tran = Transform::from_xyz(2.0, 2.5, -9.5);
|
|
||||||
light_tran.scale = Vec3::new(0.5, 0.5, 0.5);
|
|
||||||
world.spawn((
|
|
||||||
PointLight {
|
|
||||||
color: Vec3::new(0.0, 0.0, 1.0),
|
|
||||||
|
|
||||||
intensity: 3.3,
|
|
||||||
|
|
||||||
constant: 1.0,
|
|
||||||
linear: 0.09,
|
|
||||||
quadratic: 0.032,
|
|
||||||
|
|
||||||
ambient: 0.2,
|
|
||||||
diffuse: 1.0,
|
|
||||||
specular: 1.3,
|
|
||||||
},
|
|
||||||
Transform::from(light_tran),
|
|
||||||
ModelComponent(cube_model),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut camera = CameraComponent::new_3d();
|
let mut camera = CameraComponent::new_3d();
|
||||||
camera.transform.translation += math::Vec3::new(0.0, 0.0, 5.5);
|
camera.transform.translation += math::Vec3::new(0.0, 0.0, 5.5);
|
||||||
world.spawn(( camera, FreeFlyCamera::default() ));
|
world.spawn(( camera, FreeFlyCamera::default() ));
|
||||||
|
@ -211,7 +233,7 @@ async fn main() {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
};
|
};
|
||||||
let fps_plugin = move |game: &mut Game| {
|
let _fps_plugin = move |game: &mut Game| {
|
||||||
let world = game.world_mut();
|
let world = game.world_mut();
|
||||||
world.add_resource(fps_counter::FPSCounter::new());
|
world.add_resource(fps_counter::FPSCounter::new());
|
||||||
};
|
};
|
||||||
|
@ -246,54 +268,54 @@ async fn main() {
|
||||||
};
|
};
|
||||||
|
|
||||||
let action_handler_plugin = |game: &mut Game| {
|
let action_handler_plugin = |game: &mut Game| {
|
||||||
/* let action_handler = ActionHandler::builder()
|
let action_handler = ActionHandler::builder()
|
||||||
.add_layout(LayoutId::from(0))
|
.add_layout(LayoutId::from(0))
|
||||||
|
|
||||||
.add_action(CommonActionLabel::MoveForwardBackward, Action::new(ActionKind::Axis))
|
.add_action(ACTLBL_MOVE_FORWARD_BACKWARD, Action::new(ActionKind::Axis))
|
||||||
.add_action(CommonActionLabel::MoveLeftRight, Action::new(ActionKind::Axis))
|
.add_action(ACTLBL_MOVE_LEFT_RIGHT, Action::new(ActionKind::Axis))
|
||||||
.add_action(CommonActionLabel::MoveUpDown, Action::new(ActionKind::Axis))
|
.add_action(ACTLBL_MOVE_UP_DOWN, Action::new(ActionKind::Axis))
|
||||||
.add_action(CommonActionLabel::LookLeftRight, Action::new(ActionKind::Axis))
|
.add_action(ACTLBL_LOOK_LEFT_RIGHT, Action::new(ActionKind::Axis))
|
||||||
.add_action(CommonActionLabel::LookUpDown, Action::new(ActionKind::Axis))
|
.add_action(ACTLBL_LOOK_UP_DOWN, Action::new(ActionKind::Axis))
|
||||||
.add_action(CommonActionLabel::LookRoll, Action::new(ActionKind::Axis))
|
.add_action(ACTLBL_LOOK_ROLL, Action::new(ActionKind::Axis))
|
||||||
|
|
||||||
.add_mapping(ActionMapping::builder(LayoutId::from(0), ActionMappingId::from(0))
|
.add_mapping(ActionMapping::builder(LayoutId::from(0), ActionMappingId::from(0))
|
||||||
.bind(CommonActionLabel::MoveForwardBackward, &[
|
.bind(ACTLBL_MOVE_FORWARD_BACKWARD, &[
|
||||||
ActionSource::Keyboard(KeyCode::W).into_binding_modifier(1.0),
|
ActionSource::Keyboard(KeyCode::W).into_binding_modifier(1.0),
|
||||||
ActionSource::Keyboard(KeyCode::S).into_binding_modifier(-1.0)
|
ActionSource::Keyboard(KeyCode::S).into_binding_modifier(-1.0)
|
||||||
])
|
])
|
||||||
.bind(CommonActionLabel::MoveLeftRight, &[
|
.bind(ACTLBL_MOVE_LEFT_RIGHT, &[
|
||||||
ActionSource::Keyboard(KeyCode::A).into_binding_modifier(-1.0),
|
ActionSource::Keyboard(KeyCode::A).into_binding_modifier(-1.0),
|
||||||
ActionSource::Keyboard(KeyCode::D).into_binding_modifier(1.0)
|
ActionSource::Keyboard(KeyCode::D).into_binding_modifier(1.0)
|
||||||
])
|
])
|
||||||
.bind(CommonActionLabel::MoveUpDown, &[
|
.bind(ACTLBL_MOVE_UP_DOWN, &[
|
||||||
ActionSource::Keyboard(KeyCode::C).into_binding_modifier(1.0),
|
ActionSource::Keyboard(KeyCode::C).into_binding_modifier(1.0),
|
||||||
ActionSource::Keyboard(KeyCode::Z).into_binding_modifier(-1.0)
|
ActionSource::Keyboard(KeyCode::Z).into_binding_modifier(-1.0)
|
||||||
])
|
])
|
||||||
.bind(CommonActionLabel::LookLeftRight, &[
|
.bind(ACTLBL_LOOK_LEFT_RIGHT, &[
|
||||||
ActionSource::Mouse(MouseInput::Axis(MouseAxis::X)).into_binding(),
|
ActionSource::Mouse(MouseInput::Axis(MouseAxis::X)).into_binding(),
|
||||||
ActionSource::Keyboard(KeyCode::Left).into_binding_modifier(-1.0),
|
ActionSource::Keyboard(KeyCode::Left).into_binding_modifier(-1.0),
|
||||||
ActionSource::Keyboard(KeyCode::Right).into_binding_modifier(1.0),
|
ActionSource::Keyboard(KeyCode::Right).into_binding_modifier(1.0),
|
||||||
//ActionSource::Gamepad(GamepadFormat::DualAxis, GamepadInput::Axis(GamepadAxis::RThumbstickX)).into_binding(),
|
//ActionSource::Gamepad(GamepadFormat::DualAxis, GamepadInput::Axis(GamepadAxis::RThumbstickX)).into_binding(),
|
||||||
])
|
])
|
||||||
.bind(CommonActionLabel::LookUpDown, &[
|
.bind(ACTLBL_LOOK_UP_DOWN, &[
|
||||||
ActionSource::Mouse(MouseInput::Axis(MouseAxis::Y)).into_binding(),
|
ActionSource::Mouse(MouseInput::Axis(MouseAxis::Y)).into_binding(),
|
||||||
ActionSource::Keyboard(KeyCode::Up).into_binding_modifier(-1.0),
|
ActionSource::Keyboard(KeyCode::Up).into_binding_modifier(-1.0),
|
||||||
ActionSource::Keyboard(KeyCode::Down).into_binding_modifier(1.0),
|
ActionSource::Keyboard(KeyCode::Down).into_binding_modifier(1.0),
|
||||||
//ActionSource::Gamepad(GamepadFormat::DualAxis, GamepadInput::Axis(GamepadAxis::RThumbstickY)).into_binding(),
|
//ActionSource::Gamepad(GamepadFormat::DualAxis, GamepadInput::Axis(GamepadAxis::RThumbstickY)).into_binding(),
|
||||||
])
|
])
|
||||||
.bind(CommonActionLabel::LookRoll, &[
|
.bind(ACTLBL_LOOK_ROLL, &[
|
||||||
ActionSource::Keyboard(KeyCode::E).into_binding_modifier(-1.0),
|
ActionSource::Keyboard(KeyCode::E).into_binding_modifier(-1.0),
|
||||||
ActionSource::Keyboard(KeyCode::Q).into_binding_modifier(1.0),
|
ActionSource::Keyboard(KeyCode::Q).into_binding_modifier(1.0),
|
||||||
])
|
])
|
||||||
.finish()
|
.finish()
|
||||||
);
|
).finish();
|
||||||
|
|
||||||
let world = game.world_mut();
|
let world = game.world_mut();
|
||||||
world.add_resource(action_handler); */
|
world.add_resource(action_handler);
|
||||||
game.with_plugin(InputActionPlugin);
|
game.with_plugin(InputActionPlugin);
|
||||||
};
|
};
|
||||||
|
|
||||||
let script_test_plugin = |game: &mut Game| {
|
/* let script_test_plugin = |game: &mut Game| {
|
||||||
game.with_plugin(LuaScriptingPlugin);
|
game.with_plugin(LuaScriptingPlugin);
|
||||||
|
|
||||||
let world = game.world_mut();
|
let world = game.world_mut();
|
||||||
|
@ -306,13 +328,13 @@ async fn main() {
|
||||||
let scripts = ScriptList::new(vec![script]);
|
let scripts = ScriptList::new(vec![script]);
|
||||||
world.spawn((scripts,));
|
world.spawn((scripts,));
|
||||||
|
|
||||||
};
|
}; */
|
||||||
|
|
||||||
Game::initialize().await
|
Game::initialize().await
|
||||||
.with_plugin(lyra_engine::DefaultPlugins)
|
.with_plugin(lyra_engine::DefaultPlugins)
|
||||||
.with_startup_system(setup_sys.into_system())
|
.with_startup_system(setup_sys.into_system())
|
||||||
.with_plugin(action_handler_plugin)
|
.with_plugin(action_handler_plugin)
|
||||||
.with_plugin(script_test_plugin)
|
//.with_plugin(script_test_plugin)
|
||||||
//.with_plugin(fps_plugin)
|
//.with_plugin(fps_plugin)
|
||||||
.with_plugin(jiggle_plugin)
|
.with_plugin(jiggle_plugin)
|
||||||
.with_plugin(FreeFlyCameraPlugin)
|
.with_plugin(FreeFlyCameraPlugin)
|
||||||
|
|
|
@ -10,11 +10,11 @@ pub struct MaterialSpecular {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MaterialSpecular {
|
impl MaterialSpecular {
|
||||||
pub fn from_resource(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Arc<wgpu::BindGroupLayout>, value: &lyra_resource::Specular) -> Self {
|
pub fn from_resource(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Arc<wgpu::BindGroupLayout>, value: &lyra_resource::gltf::Specular) -> Self {
|
||||||
let tex = value.texture.as_ref().map(|t| t.data_ref())
|
let tex = value.texture.as_ref().and_then(|t| t.data_ref())
|
||||||
.map(|i| RenderTexture::from_image(device, queue, bg_layout.clone(), &i.image, None).unwrap());
|
.map(|i| RenderTexture::from_image(device, queue, bg_layout.clone(), &i.image, None).unwrap());
|
||||||
|
|
||||||
let color_tex = value.color_texture.as_ref().map(|t| t.data_ref())
|
let color_tex = value.color_texture.as_ref().and_then(|t| t.data_ref())
|
||||||
.map(|i| RenderTexture::from_image(device, queue, bg_layout, &i.image, None).unwrap());
|
.map(|i| RenderTexture::from_image(device, queue, bg_layout, &i.image, None).unwrap());
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
@ -38,8 +38,8 @@ pub struct Material {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Material {
|
impl Material {
|
||||||
pub fn from_resource(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Arc<wgpu::BindGroupLayout>, value: &lyra_resource::Material) -> Self {
|
pub fn from_resource(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Arc<wgpu::BindGroupLayout>, value: &lyra_resource::gltf::Material) -> Self {
|
||||||
let diffuse_texture = value.base_color_texture.as_ref().map(|t| t.data_ref())
|
let diffuse_texture = value.base_color_texture.as_ref().and_then(|t| t.data_ref())
|
||||||
.map(|i| RenderTexture::from_image(device, queue, bg_layout.clone(), &i.image, None).unwrap());
|
.map(|i| RenderTexture::from_image(device, queue, bg_layout.clone(), &i.image, None).unwrap());
|
||||||
|
|
||||||
let specular = value.specular.as_ref().map(|s| MaterialSpecular::from_resource(device, queue, bg_layout.clone(), s));
|
let specular = value.specular.as_ref().map(|s| MaterialSpecular::from_resource(device, queue, bg_layout.clone(), s));
|
||||||
|
|
|
@ -5,10 +5,13 @@ use std::borrow::Cow;
|
||||||
use glam::Vec3;
|
use glam::Vec3;
|
||||||
use instant::Instant;
|
use instant::Instant;
|
||||||
use itertools::izip;
|
use itertools::izip;
|
||||||
use lyra_ecs::Entity;
|
use lyra_ecs::query::filter::{Has, Or};
|
||||||
|
use lyra_ecs::{Entity, Tick};
|
||||||
use lyra_ecs::query::{Entities, TickOf};
|
use lyra_ecs::query::{Entities, TickOf};
|
||||||
use lyra_ecs::World;
|
use lyra_ecs::World;
|
||||||
use tracing::{debug, warn};
|
use lyra_resource::gltf::GltfScene;
|
||||||
|
use tracing::{debug, debug_span, warn};
|
||||||
|
use uuid::Uuid;
|
||||||
use wgpu::{BindGroupLayout, Limits};
|
use wgpu::{BindGroupLayout, Limits};
|
||||||
use wgpu::util::DeviceExt;
|
use wgpu::util::DeviceExt;
|
||||||
use winit::window::Window;
|
use winit::window::Window;
|
||||||
|
@ -16,7 +19,7 @@ use winit::window::Window;
|
||||||
use crate::math::Transform;
|
use crate::math::Transform;
|
||||||
use crate::render::material::MaterialUniform;
|
use crate::render::material::MaterialUniform;
|
||||||
use crate::render::render_buffer::BufferWrapperBuilder;
|
use crate::render::render_buffer::BufferWrapperBuilder;
|
||||||
use crate::scene::{ModelComponent, CameraComponent};
|
use crate::scene::CameraComponent;
|
||||||
|
|
||||||
use super::camera::{RenderCamera, CameraUniform};
|
use super::camera::{RenderCamera, CameraUniform};
|
||||||
use super::desc_buf_lay::DescVertexBufferLayout;
|
use super::desc_buf_lay::DescVertexBufferLayout;
|
||||||
|
@ -28,7 +31,10 @@ use super::transform_buffer_storage::TransformBuffers;
|
||||||
use super::vertex::Vertex;
|
use super::vertex::Vertex;
|
||||||
use super::{render_pipeline::FullRenderPipeline, render_buffer::BufferStorage, render_job::RenderJob};
|
use super::{render_pipeline::FullRenderPipeline, render_buffer::BufferStorage, render_job::RenderJob};
|
||||||
|
|
||||||
use lyra_resource::Mesh;
|
use lyra_resource::{gltf::Mesh, ResHandle};
|
||||||
|
|
||||||
|
type MeshHandle = ResHandle<Mesh>;
|
||||||
|
type SceneHandle = ResHandle<GltfScene>;
|
||||||
|
|
||||||
pub trait Renderer {
|
pub trait Renderer {
|
||||||
fn prepare(&mut self, main_world: &mut World);
|
fn prepare(&mut self, main_world: &mut World);
|
||||||
|
@ -265,16 +271,19 @@ impl BasicRenderer {
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_mesh_buffers(&mut self, _entity: Entity, mesh: &Mesh) {
|
/// Checks if the mesh buffers in the GPU need to be updated.
|
||||||
if let Some(buffers) = self.mesh_buffers.get_mut(&mesh.uuid) {
|
fn check_mesh_buffers(&mut self, _entity: Entity, meshh: &ResHandle<Mesh>) {
|
||||||
|
let mesh_uuid = meshh.uuid();
|
||||||
|
|
||||||
|
if let (Some(mesh), Some(buffers)) = (meshh.data_ref(), self.mesh_buffers.get_mut(&mesh_uuid)) {
|
||||||
// check if the buffer sizes dont match. If they dont, completely remake the buffers
|
// check if the buffer sizes dont match. If they dont, completely remake the buffers
|
||||||
let vertices = mesh.position().unwrap();
|
let vertices = mesh.position().unwrap();
|
||||||
if buffers.buffer_vertex.count() != vertices.len() {
|
if buffers.buffer_vertex.count() != vertices.len() {
|
||||||
debug!("Recreating buffers for mesh {}", mesh.uuid.to_string());
|
debug!("Recreating buffers for mesh {}", mesh_uuid.to_string());
|
||||||
let (vert, idx) = self.create_vertex_index_buffers(mesh);
|
let (vert, idx) = self.create_vertex_index_buffers(&mesh);
|
||||||
|
|
||||||
// have to re-get buffers because of borrow checker
|
// have to re-get buffers because of borrow checker
|
||||||
let buffers = self.mesh_buffers.get_mut(&mesh.uuid).unwrap();
|
let buffers = self.mesh_buffers.get_mut(&mesh_uuid).unwrap();
|
||||||
buffers.buffer_indices = idx;
|
buffers.buffer_indices = idx;
|
||||||
buffers.buffer_vertex = vert;
|
buffers.buffer_vertex = vert;
|
||||||
|
|
||||||
|
@ -292,8 +301,8 @@ impl BasicRenderer {
|
||||||
if let Some(index_buffer) = buffers.buffer_indices.as_ref() {
|
if let Some(index_buffer) = buffers.buffer_indices.as_ref() {
|
||||||
let aligned_indices = match mesh.indices.as_ref().unwrap() {
|
let aligned_indices = match mesh.indices.as_ref().unwrap() {
|
||||||
// U16 indices need to be aligned to u32, for wpgu, which are 4-bytes in size.
|
// U16 indices need to be aligned to u32, for wpgu, which are 4-bytes in size.
|
||||||
lyra_resource::MeshIndices::U16(v) => bytemuck::pod_align_to::<u16, u32>(v).1,
|
lyra_resource::gltf::MeshIndices::U16(v) => bytemuck::pod_align_to::<u16, u32>(v).1,
|
||||||
lyra_resource::MeshIndices::U32(v) => bytemuck::pod_align_to::<u32, u32>(v).1,
|
lyra_resource::gltf::MeshIndices::U32(v) => bytemuck::pod_align_to::<u32, u32>(v).1,
|
||||||
};
|
};
|
||||||
|
|
||||||
let index_buffer = index_buffer.1.buffer();
|
let index_buffer = index_buffer.1.buffer();
|
||||||
|
@ -327,8 +336,8 @@ impl BasicRenderer {
|
||||||
let indices = match mesh.indices.as_ref() {
|
let indices = match mesh.indices.as_ref() {
|
||||||
Some(indices) => {
|
Some(indices) => {
|
||||||
let (idx_type, len, contents) = match indices {
|
let (idx_type, len, contents) = match indices {
|
||||||
lyra_resource::MeshIndices::U16(v) => (wgpu::IndexFormat::Uint16, v.len(), bytemuck::cast_slice(v)),
|
lyra_resource::gltf::MeshIndices::U16(v) => (wgpu::IndexFormat::Uint16, v.len(), bytemuck::cast_slice(v)),
|
||||||
lyra_resource::MeshIndices::U32(v) => (wgpu::IndexFormat::Uint32, v.len(), bytemuck::cast_slice(v)),
|
lyra_resource::gltf::MeshIndices::U32(v) => (wgpu::IndexFormat::Uint32, v.len(), bytemuck::cast_slice(v)),
|
||||||
};
|
};
|
||||||
|
|
||||||
let index_buffer = self.device.create_buffer_init(
|
let index_buffer = self.device.create_buffer_init(
|
||||||
|
@ -354,7 +363,11 @@ impl BasicRenderer {
|
||||||
fn create_mesh_buffers(&mut self, mesh: &Mesh) -> MeshBufferStorage {
|
fn create_mesh_buffers(&mut self, mesh: &Mesh) -> MeshBufferStorage {
|
||||||
let (vertex_buffer, buffer_indices) = self.create_vertex_index_buffers(mesh);
|
let (vertex_buffer, buffer_indices) = self.create_vertex_index_buffers(mesh);
|
||||||
|
|
||||||
let material = Material::from_resource(&self.device, &self.queue, self.bgl_texture.clone(), &mesh.material());
|
let material = mesh.material.as_ref()
|
||||||
|
.expect("Material resource not loaded yet")
|
||||||
|
.data_ref()
|
||||||
|
.unwrap();
|
||||||
|
let material = Material::from_resource(&self.device, &self.queue, self.bgl_texture.clone(), &material);
|
||||||
let uni = MaterialUniform::from(&material);
|
let uni = MaterialUniform::from(&material);
|
||||||
self.queue.write_buffer(&self.material_buffer.inner_buf, 0, bytemuck::bytes_of(&uni));
|
self.queue.write_buffer(&self.material_buffer.inner_buf, 0, bytemuck::bytes_of(&uni));
|
||||||
debug!("Wrote material to buffer");
|
debug!("Wrote material to buffer");
|
||||||
|
@ -367,7 +380,7 @@ impl BasicRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes the mesh for the renderer, storing and creating buffers as needed. Returns true if a new mesh was processed.
|
/// Processes the mesh for the renderer, storing and creating buffers as needed. Returns true if a new mesh was processed.
|
||||||
fn process_mesh(&mut self, entity: Entity, transform: Transform, mesh: &Mesh) -> bool {
|
fn process_mesh(&mut self, entity: Entity, transform: Transform, mesh: &Mesh, mesh_uuid: Uuid) -> bool {
|
||||||
if self.transform_buffers.should_expand() {
|
if self.transform_buffers.should_expand() {
|
||||||
self.transform_buffers.expand_buffers(&self.device);
|
self.transform_buffers.expand_buffers(&self.device);
|
||||||
}
|
}
|
||||||
|
@ -376,15 +389,48 @@ impl BasicRenderer {
|
||||||
entity, || ( transform.calculate_mat4(), glam::Mat3::from_quat(transform.rotation) ));
|
entity, || ( transform.calculate_mat4(), glam::Mat3::from_quat(transform.rotation) ));
|
||||||
|
|
||||||
#[allow(clippy::map_entry)]
|
#[allow(clippy::map_entry)]
|
||||||
if !self.mesh_buffers.contains_key(&mesh.uuid) {
|
if !self.mesh_buffers.contains_key(&mesh_uuid) {
|
||||||
// create the mesh's buffers
|
// create the mesh's buffers
|
||||||
let buffers = self.create_mesh_buffers(mesh);
|
let buffers = self.create_mesh_buffers(mesh);
|
||||||
self.mesh_buffers.insert(mesh.uuid, buffers);
|
self.mesh_buffers.insert(mesh_uuid, buffers);
|
||||||
self.entity_meshes.insert(entity, mesh.uuid);
|
self.entity_meshes.insert(entity, mesh_uuid);
|
||||||
|
|
||||||
true
|
true
|
||||||
} else { false }
|
} else { false }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn interpolate_transforms(&mut self, now: Instant, last_epoch: Tick, entity: Entity, transform: &Transform, transform_epoch: Tick) -> Transform {
|
||||||
|
let cached = match self.entity_last_transforms.get_mut(&entity) {
|
||||||
|
Some(last) if transform_epoch == last_epoch => {
|
||||||
|
last.from_transform = last.to_transform;
|
||||||
|
last.to_transform = *transform;
|
||||||
|
last.last_updated_at = Some(last.cached_at);
|
||||||
|
last.cached_at = now;
|
||||||
|
|
||||||
|
last.clone()
|
||||||
|
},
|
||||||
|
Some(last) => last.clone(),
|
||||||
|
None => {
|
||||||
|
let cached = CachedTransform {
|
||||||
|
last_updated_at: None,
|
||||||
|
cached_at: now,
|
||||||
|
from_transform: *transform,
|
||||||
|
to_transform: *transform,
|
||||||
|
};
|
||||||
|
self.entity_last_transforms.insert(entity, cached.clone());
|
||||||
|
cached
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let fixed_time = match cached.last_updated_at {
|
||||||
|
Some(last_updated_at) => cached.cached_at - last_updated_at,
|
||||||
|
None => now - cached.cached_at
|
||||||
|
}.as_secs_f32();
|
||||||
|
let accumulator = (now - cached.cached_at).as_secs_f32();
|
||||||
|
let alpha = accumulator / fixed_time;
|
||||||
|
|
||||||
|
cached.from_transform.lerp(cached.to_transform, alpha)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Renderer for BasicRenderer {
|
impl Renderer for BasicRenderer {
|
||||||
|
@ -395,57 +441,71 @@ impl Renderer for BasicRenderer {
|
||||||
|
|
||||||
let now_inst = Instant::now();
|
let now_inst = Instant::now();
|
||||||
|
|
||||||
for (entity, model, model_epoch, transform, transform_epoch) in main_world.view_iter::<(Entities, &ModelComponent, TickOf<ModelComponent>, &Transform, TickOf<Transform>)>() {
|
let view = main_world.filtered_view_iter::<(Entities, &Transform, TickOf<Transform>), Or<Has<MeshHandle>, Has<SceneHandle>>>();
|
||||||
|
for (entity, transform, transform_epoch) in view {
|
||||||
alive_entities.insert(entity);
|
alive_entities.insert(entity);
|
||||||
|
|
||||||
let cached = match self.entity_last_transforms.get_mut(&entity) {
|
let mesh_view = main_world.view_one::<(&MeshHandle, TickOf<MeshHandle>)>(entity);
|
||||||
Some(last) if transform_epoch == last_epoch => {
|
if let Some((mesh_han, mesh_epoch)) = mesh_view.get() {
|
||||||
last.from_transform = last.to_transform;
|
let interop_pos = self.interpolate_transforms(now_inst, last_epoch, entity, &transform, transform_epoch);
|
||||||
last.to_transform = *transform;
|
|
||||||
last.last_updated_at = Some(last.cached_at);
|
|
||||||
last.cached_at = now_inst;
|
|
||||||
|
|
||||||
last.clone()
|
if let Some(mesh) = mesh_han.data_ref() {
|
||||||
},
|
// if process mesh did not just create a new mesh, and the epoch
|
||||||
Some(last) => last.clone(),
|
// shows that the scene has changed, verify that the mesh buffers
|
||||||
None => {
|
// dont need to be resent to the gpu.
|
||||||
let cached = CachedTransform {
|
if !self.process_mesh(entity, interop_pos, &*mesh, mesh_han.uuid())
|
||||||
last_updated_at: None,
|
&& mesh_epoch == last_epoch {
|
||||||
cached_at: now_inst,
|
self.check_mesh_buffers(entity, &mesh_han);
|
||||||
from_transform: *transform,
|
}
|
||||||
to_transform: *transform,
|
|
||||||
};
|
let material = mesh.material.as_ref().unwrap()
|
||||||
self.entity_last_transforms.insert(entity, cached.clone());
|
.data_ref().unwrap();
|
||||||
cached
|
let shader = material.shader_uuid.unwrap_or(0);
|
||||||
|
let job = RenderJob::new(entity, shader, mesh_han.uuid(), interop_pos);
|
||||||
|
self.render_jobs.push_back(job);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
//debug!("Transform: {:?}, comp: {:?}", cached.to_transform.translation, transform.transform.translation);
|
|
||||||
|
|
||||||
let fixed_time = match cached.last_updated_at {
|
|
||||||
Some(last_updated_at) => cached.cached_at - last_updated_at,
|
|
||||||
None => now_inst - cached.cached_at
|
|
||||||
}.as_secs_f32();
|
|
||||||
let accumulator = (now_inst - cached.cached_at).as_secs_f32();
|
|
||||||
let alpha = accumulator / fixed_time;
|
|
||||||
|
|
||||||
let transform_val = cached.from_transform.lerp(cached.to_transform, alpha);
|
let scene_view = main_world.view_one::<(&SceneHandle, TickOf<SceneHandle>)>(entity);
|
||||||
|
if let Some((scene_han, scene_epoch)) = scene_view.get() {
|
||||||
|
// TODO: Rendering scenes
|
||||||
|
|
||||||
let model = model.data_ref();
|
let scene_scope = debug_span!("scene", uuid = scene_han.uuid().to_string());
|
||||||
for mesh in model.meshes.iter() {
|
let _enter = scene_scope.enter();
|
||||||
if !self.process_mesh(entity, transform_val, mesh) && model_epoch == last_epoch {
|
|
||||||
self.update_mesh_buffers(entity, mesh);
|
if let Some(scene) = scene_han.data_ref() {
|
||||||
|
let interop_pos = self.interpolate_transforms(now_inst, last_epoch, entity, &transform, transform_epoch);
|
||||||
|
let meshes = scene.collect_world_meshes();
|
||||||
|
|
||||||
|
for (mesh_han, mesh_pos) in meshes.into_iter() {
|
||||||
|
if let Some(mesh) = mesh_han.data_ref() {
|
||||||
|
let mesh_interp = interop_pos + mesh_pos;
|
||||||
|
|
||||||
|
let mesh_scope = debug_span!("mesh", uuid = mesh_han.uuid().to_string());
|
||||||
|
let _enter = mesh_scope.enter();
|
||||||
|
|
||||||
|
debug!("mesh at {:?}", mesh_interp.translation);
|
||||||
|
|
||||||
|
// if process mesh did not just create a new mesh, and the epoch
|
||||||
|
// shows that the scene has changed, verify that the mesh buffers
|
||||||
|
// dont need to be resent to the gpu.
|
||||||
|
if !self.process_mesh(entity, mesh_interp, &*mesh, mesh_han.uuid())
|
||||||
|
&& scene_epoch == last_epoch {
|
||||||
|
self.check_mesh_buffers(entity, &mesh_han);
|
||||||
|
debug!("updating mesh buffers");
|
||||||
|
}
|
||||||
|
|
||||||
|
let material = mesh.material.as_ref().unwrap()
|
||||||
|
.data_ref().unwrap();
|
||||||
|
let shader = material.shader_uuid.unwrap_or(0);
|
||||||
|
let job = RenderJob::new(entity, shader, mesh_han.uuid(), interop_pos);
|
||||||
|
self.render_jobs.push_back(job);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let shader = mesh.material().shader_uuid.unwrap_or(0);
|
|
||||||
let job = RenderJob::new(entity, shader, mesh.uuid, transform_val);
|
|
||||||
self.render_jobs.push_back(job);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* for (entity, mesh, mesh_epoch, transform) in main_world.query::<(Entities, &MeshComponent, EpochOf<MeshComponent>, &TransformComponent)>().iter() {
|
|
||||||
debug!("TODO: Process MeshComponents"); // TODO: Process MeshComponents
|
|
||||||
} */
|
|
||||||
|
|
||||||
// collect dead entities
|
// collect dead entities
|
||||||
self.transform_buffers.tick();
|
self.transform_buffers.tick();
|
||||||
|
|
||||||
|
|
|
@ -5,25 +5,6 @@ use lyra_resource::{ResHandle, Texture};
|
||||||
|
|
||||||
use super::render_buffer::BindGroupPair;
|
use super::render_buffer::BindGroupPair;
|
||||||
|
|
||||||
/* #[derive(Clone)]
|
|
||||||
pub struct Texture {
|
|
||||||
texture_id: u32,
|
|
||||||
pub img: image::DynamicImage,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Texture {
|
|
||||||
pub fn from_bytes(bytes: &[u8]) -> anyhow::Result<Self> {
|
|
||||||
let img = image::load_from_memory(bytes)?;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
texture_id: 0,
|
|
||||||
img,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub struct RenderTexture {
|
pub struct RenderTexture {
|
||||||
pub inner_texture: wgpu::Texture,
|
pub inner_texture: wgpu::Texture,
|
||||||
|
@ -153,8 +134,12 @@ impl RenderTexture {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_texture(&mut self, _device: &wgpu::Device, queue: &wgpu::Queue, texture: &Arc<ResHandle<Texture>>) {
|
/// Updates the texture on the gpu with the provided texture.
|
||||||
let texture = &texture.data_ref().image;
|
///
|
||||||
|
/// # Panics
|
||||||
|
/// Panics if `texture` is not loaded
|
||||||
|
pub fn update_texture(&mut self, _device: &wgpu::Device, queue: &wgpu::Queue, texture: &ResHandle<Texture>) {
|
||||||
|
let texture = &texture.data_ref().unwrap().image;
|
||||||
let rgba = texture.to_rgba8();
|
let rgba = texture.to_rgba8();
|
||||||
let dimensions = texture.dimensions();
|
let dimensions = texture.dimensions();
|
||||||
let size = wgpu::Extent3d {
|
let size = wgpu::Extent3d {
|
||||||
|
|
|
@ -1,15 +1,37 @@
|
||||||
use lyra_ecs::Component;
|
use lyra_ecs::Component;
|
||||||
use lyra_resource::Mesh;
|
use lyra_reflect::Reflect;
|
||||||
|
use lyra_resource::{gltf::Mesh, ResHandle};
|
||||||
|
|
||||||
#[derive(Clone, Component)]
|
#[derive(Clone, Component, Reflect)]
|
||||||
pub struct MeshComponent {
|
pub struct MeshComponent {
|
||||||
pub mesh: Mesh,
|
#[reflect(skip)]
|
||||||
|
pub mesh: ResHandle<Mesh>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ResHandle<Mesh>> for MeshComponent {
|
||||||
|
fn from(value: ResHandle<Mesh>) -> Self {
|
||||||
|
Self {
|
||||||
|
mesh: value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Deref for MeshComponent {
|
||||||
|
type Target = ResHandle<Mesh>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.mesh
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::DerefMut for MeshComponent {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.mesh
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MeshComponent {
|
impl MeshComponent {
|
||||||
pub fn new(mesh: Mesh) -> Self {
|
pub fn new(mesh: ResHandle<Mesh>) -> Self {
|
||||||
Self {
|
Self::from(mesh)
|
||||||
mesh,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,9 +1,6 @@
|
||||||
pub mod mesh;
|
pub mod mesh;
|
||||||
pub use mesh::*;
|
pub use mesh::*;
|
||||||
|
|
||||||
pub mod model;
|
|
||||||
pub use model::*;
|
|
||||||
|
|
||||||
pub mod camera;
|
pub mod camera;
|
||||||
pub use camera::*;
|
pub use camera::*;
|
||||||
|
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
use lyra_ecs::Component;
|
|
||||||
use lyra_reflect::Reflect;
|
|
||||||
use lyra_resource::ResHandle;
|
|
||||||
|
|
||||||
use crate::assets::Model;
|
|
||||||
|
|
||||||
#[derive(Clone, Component, Reflect)]
|
|
||||||
pub struct ModelComponent(#[reflect(skip)] pub ResHandle<Model>);
|
|
||||||
|
|
||||||
impl From<ResHandle<Model>> for ModelComponent {
|
|
||||||
fn from(value: ResHandle<Model>) -> Self {
|
|
||||||
ModelComponent(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* impl From<ResHandle<Model> for ModelComponent {
|
|
||||||
|
|
||||||
} */
|
|
||||||
|
|
||||||
impl std::ops::Deref for ModelComponent {
|
|
||||||
type Target = ResHandle<Model>;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::DerefMut for ModelComponent {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* impl ModelComponent {
|
|
||||||
pub fn new(model, material: Material) -> Self {
|
|
||||||
Self {
|
|
||||||
mesh,
|
|
||||||
material
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} */
|
|
Loading…
Reference in New Issue