mirror of
https://github.com/FyroxEngine/Fyrox.git
synced 2026-10-12 02:02:20 +00:00
293 lines
9.1 KiB
Rust
293 lines
9.1 KiB
Rust
//! Example 12. Custom loader
|
|
//!
|
|
//! Difficulty: Moderate.
|
|
//!
|
|
//! This example shows how to create a custom loader. It is a very basic example and in future it should be improved by
|
|
//! writing some more complex loader such as loading a model from ply or obj file.
|
|
|
|
/// For simplicity we just simply wrap the default loader and log the invocation t
|
|
pub mod shared;
|
|
|
|
use crate::shared::create_camera;
|
|
use fyrox::resource::model::{Model, ModelResourceExtension};
|
|
use fyrox::resource::texture::Texture;
|
|
use fyrox::{
|
|
asset::{
|
|
container::event::ResourceEventBroadcaster,
|
|
loader::{BoxedLoaderFuture, ResourceLoader},
|
|
manager::ResourceManager,
|
|
},
|
|
core::{
|
|
algebra::{Matrix4, Vector3},
|
|
color::Color,
|
|
futures::executor::block_on,
|
|
instant::Instant,
|
|
sstorage::ImmutableString,
|
|
},
|
|
engine::resource_loaders::{model::ModelLoader, texture::TextureLoader},
|
|
engine::{
|
|
Engine, EngineInitParams, GraphicsContext, GraphicsContextParams, SerializationContext,
|
|
},
|
|
event::{Event, WindowEvent},
|
|
event_loop::{ControlFlow, EventLoop},
|
|
material::{shader::SamplerFallback, Material, PropertyValue, SharedMaterial},
|
|
scene::{
|
|
base::BaseBuilder,
|
|
light::{point::PointLightBuilder, BaseLightBuilder},
|
|
mesh::{
|
|
surface::{SurfaceBuilder, SurfaceData, SurfaceSharedData},
|
|
MeshBuilder,
|
|
},
|
|
transform::TransformBuilder,
|
|
Scene,
|
|
},
|
|
utils::{
|
|
log::{Log, MessageKind},
|
|
translate_event,
|
|
},
|
|
window::WindowAttributes,
|
|
};
|
|
use fyrox_resource::untyped::UntypedResource;
|
|
use std::any::Any;
|
|
use std::sync::Arc;
|
|
|
|
struct CustomModelLoader(Arc<ModelLoader>);
|
|
|
|
impl ResourceLoader for CustomModelLoader {
|
|
fn extensions(&self) -> &[&str] {
|
|
&["rgs", "fbx"]
|
|
}
|
|
|
|
fn as_any(&self) -> &dyn Any {
|
|
self
|
|
}
|
|
|
|
fn as_any_mut(&mut self) -> &mut dyn Any {
|
|
self
|
|
}
|
|
|
|
fn load(
|
|
&self,
|
|
resource: UntypedResource,
|
|
event_broadcaster: ResourceEventBroadcaster,
|
|
reload: bool,
|
|
) -> BoxedLoaderFuture {
|
|
// Arc is required as BoxedLoaderFuture has a static lifetime and hence self cannot be
|
|
// moved into an async block.
|
|
let loader = self.0.clone();
|
|
|
|
Box::pin(async move {
|
|
println!("CUSTOM LOADER: loading model {:?}", resource.path());
|
|
loader.load(resource, event_broadcaster, reload).await
|
|
})
|
|
}
|
|
}
|
|
|
|
/// For simplicity we just simply wrap the default loader and log the invocation to the console.
|
|
struct CustomTextureLoader(Arc<TextureLoader>);
|
|
|
|
impl ResourceLoader for CustomTextureLoader {
|
|
fn extensions(&self) -> &[&str] {
|
|
&["jpg", "jpeg", "tga", "gif", "bmp", "png", "tiff", "dds"]
|
|
}
|
|
|
|
fn as_any(&self) -> &dyn Any {
|
|
self
|
|
}
|
|
|
|
fn as_any_mut(&mut self) -> &mut dyn Any {
|
|
self
|
|
}
|
|
|
|
fn load(
|
|
&self,
|
|
resource: UntypedResource,
|
|
event_broadcaster: ResourceEventBroadcaster,
|
|
reload: bool,
|
|
) -> BoxedLoaderFuture {
|
|
// Arc is required as BoxedLoaderFuture has a static lifetime and hence self cannot be
|
|
// moved into an async block.
|
|
let loader = self.0.clone();
|
|
|
|
Box::pin(async move {
|
|
println!("CUSTOM LOADER: loading texture {:?}", resource.path());
|
|
loader.load(resource, event_broadcaster, reload).await
|
|
})
|
|
}
|
|
}
|
|
|
|
struct GameSceneLoader {
|
|
scene: Scene,
|
|
}
|
|
|
|
impl GameSceneLoader {
|
|
async fn load_with(resource_manager: ResourceManager) -> Self {
|
|
let mut scene = Scene::new();
|
|
|
|
// Set ambient light.
|
|
scene.ambient_lighting_color = Color::opaque(200, 200, 200);
|
|
|
|
// Camera is our eyes in the world - you won't see anything without it.
|
|
create_camera(
|
|
resource_manager.clone(),
|
|
Vector3::new(0.0, 6.0, -12.0),
|
|
&mut scene.graph,
|
|
)
|
|
.await;
|
|
|
|
// Add some light.
|
|
PointLightBuilder::new(BaseLightBuilder::new(
|
|
BaseBuilder::new().with_local_transform(
|
|
TransformBuilder::new()
|
|
.with_local_position(Vector3::new(0.0, 12.0, 0.0))
|
|
.build(),
|
|
),
|
|
))
|
|
.with_radius(20.0)
|
|
.build(&mut scene.graph);
|
|
|
|
// Add some model with animation.
|
|
let model_resource = resource_manager
|
|
.request::<Model, _>("examples/data/mutant/mutant.FBX")
|
|
.await;
|
|
|
|
let model_handle = model_resource.unwrap().instantiate(&mut scene);
|
|
scene.graph[model_handle]
|
|
.local_transform_mut()
|
|
.set_scale(Vector3::new(0.05, 0.05, 0.05));
|
|
|
|
let mut material = Material::standard();
|
|
material
|
|
.set_property(
|
|
&ImmutableString::new("diffuseTexture"),
|
|
PropertyValue::Sampler {
|
|
value: Some(
|
|
resource_manager.request::<Texture, _>("examples/data/concrete2.dds"),
|
|
),
|
|
fallback: SamplerFallback::White,
|
|
},
|
|
)
|
|
.unwrap();
|
|
|
|
// Add floor.
|
|
MeshBuilder::new(
|
|
BaseBuilder::new().with_local_transform(
|
|
TransformBuilder::new()
|
|
.with_local_position(Vector3::new(0.0, -0.25, 0.0))
|
|
.build(),
|
|
),
|
|
)
|
|
.with_surfaces(vec![SurfaceBuilder::new(SurfaceSharedData::new(
|
|
SurfaceData::make_cube(Matrix4::new_nonuniform_scaling(&Vector3::new(
|
|
25.0, 0.25, 25.0,
|
|
))),
|
|
))
|
|
.with_material(SharedMaterial::new(material))
|
|
.build()])
|
|
.build(&mut scene.graph);
|
|
|
|
Self { scene }
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
let event_loop = EventLoop::new();
|
|
let graphics_context_params = GraphicsContextParams {
|
|
window_attributes: WindowAttributes {
|
|
title: "Example - Custom Resource Loader".to_string(),
|
|
resizable: true,
|
|
..Default::default()
|
|
},
|
|
vsync: true,
|
|
};
|
|
|
|
let mut engine = Engine::new(EngineInitParams {
|
|
graphics_context_params,
|
|
resource_manager: ResourceManager::new(),
|
|
serialization_context: Arc::new(SerializationContext::new()),
|
|
})
|
|
.unwrap();
|
|
|
|
// Set up our custom loaders
|
|
{
|
|
let mut state = engine.resource_manager.state();
|
|
let containers = state.containers_mut();
|
|
if let Some(pos) = containers
|
|
.resources
|
|
.loaders
|
|
.iter()
|
|
.position(|l| (**l).as_any().downcast_ref::<ModelLoader>().is_some())
|
|
{
|
|
containers.resources.loaders[pos] =
|
|
Box::new(CustomModelLoader(Arc::new(ModelLoader {
|
|
resource_manager: engine.resource_manager.clone(),
|
|
serialization_context: engine.serialization_context.clone(),
|
|
default_import_options: Default::default(),
|
|
})));
|
|
}
|
|
|
|
if let Some(pos) = containers
|
|
.resources
|
|
.loaders
|
|
.iter()
|
|
.position(|l| (**l).as_any().downcast_ref::<TextureLoader>().is_some())
|
|
{
|
|
containers.resources.loaders[pos] =
|
|
Box::new(CustomTextureLoader(Arc::new(TextureLoader {
|
|
default_import_options: Default::default(),
|
|
})));
|
|
}
|
|
}
|
|
|
|
let scene = block_on(GameSceneLoader::load_with(engine.resource_manager.clone())).scene;
|
|
engine.scenes.add(scene);
|
|
|
|
let mut previous = Instant::now();
|
|
let fixed_timestep = 1.0 / 60.0;
|
|
let mut lag = 0.0;
|
|
|
|
event_loop.run(move |event, window_target, control_flow| match event {
|
|
Event::MainEventsCleared => {
|
|
let elapsed = previous.elapsed();
|
|
previous = Instant::now();
|
|
lag += elapsed.as_secs_f32();
|
|
while lag >= fixed_timestep {
|
|
engine.update(fixed_timestep, control_flow, &mut lag, Default::default());
|
|
lag -= fixed_timestep;
|
|
}
|
|
|
|
if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context {
|
|
ctx.window.request_redraw();
|
|
}
|
|
}
|
|
Event::Resumed => {
|
|
engine.initialize_graphics_context(window_target).unwrap();
|
|
}
|
|
Event::Suspended => {
|
|
engine.destroy_graphics_context().unwrap();
|
|
}
|
|
Event::RedrawRequested(_) => {
|
|
engine.render().unwrap();
|
|
}
|
|
Event::WindowEvent { event, .. } => {
|
|
match event {
|
|
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
|
WindowEvent::Resized(size) => {
|
|
if let Err(e) = engine.set_frame_size(size.into()) {
|
|
Log::writeln(
|
|
MessageKind::Error,
|
|
format!("Unable to set frame size: {:?}", e),
|
|
);
|
|
}
|
|
}
|
|
_ => (),
|
|
}
|
|
|
|
if let Some(os_event) = translate_event(&event) {
|
|
engine.user_interface.process_os_event(&os_event);
|
|
}
|
|
}
|
|
_ => *control_flow = ControlFlow::Poll,
|
|
});
|
|
}
|