Files
Fyrox/examples/scene.rs
T

253 lines
9.6 KiB
Rust

//! Example 05. Simple scene.
//!
//! Difficulty: Easy.
//!
//! This example shows how to load simple scene made in [Fyroxed](https://github.com/FyroxEngine/Fyrox)
//!
//! It is almost the same as Example 01, even easier.
pub mod shared;
use crate::shared::create_camera;
use fyrox::resource::model::{Model, ModelResourceExtension};
use fyrox::{
asset::manager::ResourceManager,
core::{
algebra::{UnitQuaternion, Vector3},
color::Color,
pool::Handle,
},
engine::{
Engine, EngineInitParams, GraphicsContext, GraphicsContextParams, SerializationContext,
},
event::{ElementState, Event, VirtualKeyCode, WindowEvent},
event_loop::{ControlFlow, EventLoop},
gui::{
message::MessageDirection,
text::{TextBuilder, TextMessage},
widget::WidgetBuilder,
BuildContext, UiNode,
},
scene::{base::BaseBuilder, node::Node, pivot::PivotBuilder, Scene},
utils::{
log::{Log, MessageKind},
translate_event,
},
};
use std::{sync::Arc, time::Instant};
use winit::window::WindowAttributes;
fn create_ui(ctx: &mut BuildContext) -> Handle<UiNode> {
TextBuilder::new(WidgetBuilder::new()).build(ctx)
}
struct GameScene {
scene: Scene,
pivot: Handle<Node>,
}
async fn create_scene(resource_manager: ResourceManager) -> GameScene {
let mut scene = Scene::new();
// Set ambient light.
scene.ambient_lighting_color = Color::opaque(80, 80, 80);
// Camera is our eyes in the world - you won't see anything without it.
let pivot = PivotBuilder::new(
BaseBuilder::new().with_children(&[create_camera(
resource_manager.clone(),
Vector3::new(0.0, 4.0, -8.0),
&mut scene.graph,
)
.await]),
)
.build(&mut scene.graph);
// There is no difference between scene created in Fyroxed and any other
// model file, so any scene can be used directly as resource.
resource_manager
.request::<Model, _>("examples/data/test_scene.rgs")
.await
.unwrap()
.instantiate(&mut scene);
GameScene { scene, pivot }
}
struct InputController {
rotate_left: bool,
rotate_right: bool,
}
fn main() {
let event_loop = EventLoop::new();
let graphics_context_params = GraphicsContextParams {
window_attributes: WindowAttributes {
title: "Example - Scene".to_string(),
resizable: true,
..Default::default()
},
vsync: true,
};
let serialization_context = Arc::new(SerializationContext::new());
let mut engine = Engine::new(EngineInitParams {
graphics_context_params,
resource_manager: ResourceManager::new(),
serialization_context,
})
.unwrap();
// Create simple user interface that will show some useful info.
let debug_text = create_ui(&mut engine.user_interface.build_ctx());
// Create test scene.
let GameScene { scene, pivot } =
fyrox::core::futures::executor::block_on(create_scene(engine.resource_manager.clone()));
// Add scene to engine - engine will take ownership over scene and will return
// you a handle to scene which can be used later on to borrow it and do some
// actions you need.
let scene_handle = engine.scenes.add(scene);
let mut previous = Instant::now();
let fixed_timestep = 1.0 / 60.0;
let mut lag = 0.0;
// We will rotate model using keyboard input.
let mut model_angle = 0.0f32.to_radians();
// Create input controller - it will hold information about needed actions.
let mut input_controller = InputController {
rotate_left: false,
rotate_right: false,
};
// Finally run our event loop which will respond to OS and window events and update
// engine state accordingly. Engine lets you to decide which event should be handled,
// this is minimal working example if how it should be.
event_loop.run(move |event, window_target, control_flow| {
match event {
Event::MainEventsCleared => {
// This main game loop - it has fixed time step which means that game
// code will run at fixed speed even if renderer can't give you desired
// 60 fps.
let elapsed = previous.elapsed();
previous = Instant::now();
lag += elapsed.as_secs_f32();
while lag >= fixed_timestep {
// ************************
// Put your game logic here.
// ************************
// Use stored scene handle to borrow a mutable reference of scene in
// engine.
let scene = &mut engine.scenes[scene_handle];
// Rotate model according to input controller state.
if input_controller.rotate_left {
model_angle -= 5.0f32.to_radians();
} else if input_controller.rotate_right {
model_angle += 5.0f32.to_radians();
}
scene.graph[pivot].local_transform_mut().set_rotation(
UnitQuaternion::from_axis_angle(&Vector3::y_axis(), model_angle),
);
if let GraphicsContext::Initialized(ref graphics_context) =
engine.graphics_context
{
let fps = graphics_context.renderer.get_statistics().frames_per_second;
let text = format!(
"Example 05 - Scene\nUse [A][D] keys to rotate camera.\nFPS: {}",
fps
);
engine.user_interface.send_message(TextMessage::text(
debug_text,
MessageDirection::ToWidget,
text,
));
}
engine.update(fixed_timestep, control_flow, &mut lag, Default::default());
lag -= fixed_timestep;
}
// It is very important to "pump" messages from UI. Even if don't need to
// respond to such message, you should call this method, otherwise UI
// might behave very weird.
while let Some(_ui_event) = engine.user_interface.poll_message() {
// ************************
// Put your data model synchronization code here. It should
// take message and update data in your game according to
// changes in UI.
// ************************
}
// Rendering must be explicitly requested and handled after RedrawRequested event is received.
if let GraphicsContext::Initialized(ref graphics_context) = engine.graphics_context
{
graphics_context.window.request_redraw();
}
}
Event::Resumed => {
engine.initialize_graphics_context(window_target).unwrap();
}
Event::Suspended => {
engine.destroy_graphics_context().unwrap();
}
Event::RedrawRequested(_) => {
// Run renderer at max speed - it is not tied to game code.
engine.render().unwrap();
}
Event::WindowEvent { event, .. } => {
match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(size) => {
// It is very important to handle Resized event from window, because
// renderer knows nothing about window size - it must be notified
// directly when window size has changed.
if let Err(e) = engine.set_frame_size(size.into()) {
Log::writeln(
MessageKind::Error,
format!("Unable to set frame size: {:?}", e),
);
}
}
WindowEvent::KeyboardInput { input, .. } => {
// Handle key input events via `WindowEvent`, not via `DeviceEvent` (#32)
if let Some(key_code) = input.virtual_keycode {
match key_code {
VirtualKeyCode::A => {
input_controller.rotate_left =
input.state == ElementState::Pressed
}
VirtualKeyCode::D => {
input_controller.rotate_right =
input.state == ElementState::Pressed
}
_ => (),
}
}
}
_ => (),
}
// It is very important to "feed" user interface (UI) with events coming
// from main window, otherwise UI won't respond to mouse, keyboard, or any
// other event.
if let Some(os_event) = translate_event(&event) {
engine.user_interface.process_os_event(&os_event);
}
}
Event::DeviceEvent { .. } => {
// Handle key input events via `WindowEvent`, not via `DeviceEvent` (#32)
}
_ => *control_flow = ControlFlow::Poll,
}
});
}