mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-21 01:12:40 -04:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb51d6711b | ||
|
|
dcab3bbe12 | ||
|
|
9cfa3bde0d | ||
|
|
abf358a59b | ||
|
|
7f7d4acf8b |
Generated
+2518
-134
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,5 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
|
||||
members = [
|
||||
# Types: common strongly-typed tokens, ids, payloads, etc.
|
||||
@@ -49,6 +50,7 @@ members = [
|
||||
|
||||
# Programs: Desktop apps
|
||||
"crates/programs/aichatbot-sidecar",
|
||||
"crates/programs/bevy-experiments",
|
||||
|
||||
# Services: API servers, jobs, workers, etc.
|
||||
"crates/service/job/analytics_job",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "temp-bevy"
|
||||
name = "bevy-storyengine"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
@@ -7,7 +7,12 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
bevy = "0.10.1"
|
||||
bevy_transform_gizmo = "0.7"
|
||||
bevy_mod_picking = { version = "0.13", features=["backend_raycast"]}
|
||||
bevy-inspector-egui = {version = "0.18.3", features=["highlight_changes"]}
|
||||
bevy_egui = { version = "0.20.3", default-features=false, features=["default_fonts"]}
|
||||
|
||||
[profile.release]
|
||||
opt-level = "z"
|
||||
lto = "fat"
|
||||
codegen-units = 1
|
||||
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 12 MiB |
@@ -0,0 +1,6 @@
|
||||
right.png
|
||||
left.png
|
||||
top.png
|
||||
bottom.png
|
||||
front.png
|
||||
back.png
|
||||
@@ -0,0 +1,24 @@
|
||||
#import bevy_pbr::mesh_view_bindings
|
||||
|
||||
#ifdef CUBEMAP_ARRAY
|
||||
@group(1) @binding(0)
|
||||
var base_color_texture: texture_cube_array<f32>;
|
||||
#else
|
||||
@group(1) @binding(0)
|
||||
var base_color_texture: texture_cube<f32>;
|
||||
#endif
|
||||
|
||||
@group(1) @binding(1)
|
||||
var base_color_sampler: sampler;
|
||||
|
||||
@fragment
|
||||
fn fragment(
|
||||
#import bevy_pbr::mesh_vertex_output
|
||||
) -> @location(0) vec4<f32> {
|
||||
let fragment_position_view_lh = world_position.xyz * vec3<f32>(1.0, 1.0, -1.0);
|
||||
return textureSample(
|
||||
base_color_texture,
|
||||
base_color_sampler,
|
||||
fragment_position_view_lh
|
||||
);
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.9 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 522 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
@@ -1,5 +1,14 @@
|
||||
<html>
|
||||
<head>
|
||||
<link data-trunk rel="copy-dir" href="assets"/>
|
||||
<!-- make the canvas full height -->
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<!-- disable right click (redirect it to the app) -->
|
||||
<body oncontextmenu="return false;">
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
use bevy::{
|
||||
prelude::*,
|
||||
animation::{AnimationClip, AnimationPlayer},
|
||||
};
|
||||
|
||||
#[derive(Resource)]
|
||||
pub(crate) struct Animations(Vec<Handle<AnimationClip>>);
|
||||
|
||||
pub(crate) fn animation_setup(
|
||||
mut commands: Commands,
|
||||
asset_server: Res<AssetServer>,
|
||||
) {
|
||||
commands.insert_resource(Animations(vec![
|
||||
asset_server.load("Roko_Anim_Wave_noOptimization.glb#Animation0"),
|
||||
]));
|
||||
}
|
||||
|
||||
pub(crate) fn animation_system(
|
||||
mut animation_player: Query<&mut AnimationPlayer>,
|
||||
animations: Res<Animations>,
|
||||
mut done: Local<bool>,
|
||||
) {
|
||||
if !*done {
|
||||
if let Ok(mut player) = animation_player.get_single_mut() {
|
||||
player.play(animations.0[0].clone_weak()).repeat();
|
||||
*done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,299 @@
|
||||
use bevy::render::camera::RenderTarget;
|
||||
use bevy::render::render_resource::{TextureUsages, TextureFormat, TextureDimension, Extent3d, TextureDescriptor};
|
||||
use bevy::window::CursorGrabMode;
|
||||
use bevy::{input::mouse::MouseMotion, prelude::*};
|
||||
use bevy_mod_picking::prelude::RaycastPickCamera;
|
||||
use bevy_transform_gizmo::GizmoTransformable;
|
||||
|
||||
use std::f32::consts::*;
|
||||
use std::fmt;
|
||||
|
||||
pub(crate) const RADIANS_PER_DOT: f32 = 1.0 / 180.0;
|
||||
|
||||
const RENDER_WIDTH: u32 = 1920;
|
||||
const RENDER_HEIGHT: u32 = 1080;
|
||||
|
||||
#[derive(Component)]
|
||||
pub(crate) struct RenderCam;
|
||||
|
||||
#[derive(Component)]
|
||||
pub(crate) struct ViewportCam;
|
||||
|
||||
pub(crate) fn camera_setup(
|
||||
mut commands: Commands,
|
||||
asset_server: Res<AssetServer>,
|
||||
mut images: ResMut<Assets<Image>>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
) {
|
||||
// viewport camera
|
||||
commands.spawn(((
|
||||
Camera3dBundle {
|
||||
transform: Transform::from_xyz(0.3, 2.0, 5.0).looking_at(Vec3::new(0.0, 1.0, 0.0), Vec3::Y),
|
||||
camera: Camera {
|
||||
order: 0,
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
},
|
||||
EnvironmentMapLight {
|
||||
diffuse_map: asset_server.load("pisa_diffuse_rgb9e5_zstd.ktx2"),
|
||||
specular_map: asset_server.load("pisa_specular_rgb9e5_zstd.ktx2"),
|
||||
},
|
||||
RaycastPickCamera::default(),
|
||||
bevy_transform_gizmo::GizmoPickSource::default(),
|
||||
CameraController::default(),
|
||||
), ViewportCam));
|
||||
|
||||
|
||||
|
||||
// render cam
|
||||
|
||||
|
||||
let output_texture_handle = {
|
||||
let size = Extent3d {
|
||||
width: RENDER_WIDTH,
|
||||
height: RENDER_HEIGHT,
|
||||
..default()
|
||||
};
|
||||
let mut export_texture = Image {
|
||||
texture_descriptor: TextureDescriptor {
|
||||
label: None,
|
||||
size,
|
||||
dimension: TextureDimension::D2,
|
||||
format: TextureFormat::Rgba8UnormSrgb,
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
usage: TextureUsages::COPY_DST
|
||||
| TextureUsages::COPY_SRC
|
||||
| TextureUsages::RENDER_ATTACHMENT,
|
||||
view_formats: &[],
|
||||
},
|
||||
..default()
|
||||
};
|
||||
export_texture.resize(size);
|
||||
images.add(export_texture)
|
||||
};
|
||||
|
||||
|
||||
let camera_proxy_mesh = asset_server.load("cameraProxy.glb#Mesh0/Primitive0");
|
||||
let camera_proxy_mat = materials.add(StandardMaterial {
|
||||
base_color: Color::BLACK,
|
||||
..default()
|
||||
});
|
||||
commands.spawn(((MaterialMeshBundle::<StandardMaterial> {
|
||||
mesh: camera_proxy_mesh,
|
||||
material: camera_proxy_mat,
|
||||
transform: Transform::from_xyz(0.0, 2.0, 5.0)
|
||||
.with_rotation(Quat::from_rotation_x(TAU/2.0))
|
||||
.with_scale(Vec3::new(0.2, 0.2, 0.2)),
|
||||
..default()
|
||||
}), GizmoTransformable))
|
||||
.with_children(|parent| {
|
||||
parent.spawn(((
|
||||
Camera3dBundle {
|
||||
transform: Transform::from_xyz(0.0, 0.0, 2.0).with_rotation(Quat::from_rotation_x(TAU/2.0)),
|
||||
camera: Camera {
|
||||
order: 1,
|
||||
target: RenderTarget::Image(output_texture_handle.clone()),
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
},
|
||||
EnvironmentMapLight {
|
||||
diffuse_map: asset_server.load("pisa_diffuse_rgb9e5_zstd.ktx2"),
|
||||
specular_map: asset_server.load("pisa_specular_rgb9e5_zstd.ktx2"),
|
||||
},
|
||||
), RenderCam));
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub(crate) struct CameraController {
|
||||
pub enabled: bool,
|
||||
pub initialized: bool,
|
||||
pub sensitivity: f32,
|
||||
pub key_forward: KeyCode,
|
||||
pub key_back: KeyCode,
|
||||
pub key_left: KeyCode,
|
||||
pub key_right: KeyCode,
|
||||
pub key_up: KeyCode,
|
||||
pub key_down: KeyCode,
|
||||
pub key_run: KeyCode,
|
||||
pub mouse_key_enable_mouse: MouseButton,
|
||||
pub keyboard_key_enable_mouse: KeyCode,
|
||||
pub walk_speed: f32,
|
||||
pub run_speed: f32,
|
||||
pub friction: f32,
|
||||
pub pitch: f32,
|
||||
pub yaw: f32,
|
||||
pub velocity: Vec3,
|
||||
}
|
||||
|
||||
impl Default for CameraController {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: true,
|
||||
initialized: false,
|
||||
sensitivity: 1.0,
|
||||
key_forward: KeyCode::W,
|
||||
key_back: KeyCode::S,
|
||||
key_left: KeyCode::A,
|
||||
key_right: KeyCode::D,
|
||||
key_up: KeyCode::E,
|
||||
key_down: KeyCode::Q,
|
||||
key_run: KeyCode::LShift,
|
||||
mouse_key_enable_mouse: MouseButton::Right,
|
||||
keyboard_key_enable_mouse: KeyCode::M,
|
||||
walk_speed: 5.0,
|
||||
run_speed: 15.0,
|
||||
friction: 0.5,
|
||||
pitch: 0.0,
|
||||
yaw: 0.0,
|
||||
velocity: Vec3::ZERO,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for CameraController {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"
|
||||
Freecam Controls:
|
||||
MOUSE\t- Move camera orientation
|
||||
{:?}/{:?}\t- Enable mouse movement
|
||||
{:?}{:?}\t- forward/backward
|
||||
{:?}{:?}\t- strafe left/right
|
||||
{:?}\t- 'run'
|
||||
{:?}\t- up
|
||||
{:?}\t- down",
|
||||
self.mouse_key_enable_mouse,
|
||||
self.keyboard_key_enable_mouse,
|
||||
self.key_forward,
|
||||
self.key_back,
|
||||
self.key_left,
|
||||
self.key_right,
|
||||
self.key_run,
|
||||
self.key_up,
|
||||
self.key_down
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct CameraControllerPlugin;
|
||||
|
||||
impl Plugin for CameraControllerPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_system(camera_controller);
|
||||
}
|
||||
}
|
||||
|
||||
fn camera_controller(
|
||||
time: Res<Time>,
|
||||
mut windows: Query<&mut Window>,
|
||||
mut mouse_events: EventReader<MouseMotion>,
|
||||
mouse_button_input: Res<Input<MouseButton>>,
|
||||
key_input: Res<Input<KeyCode>>,
|
||||
mut move_toggled: Local<bool>,
|
||||
mut query: Query<(&mut Transform, &mut CameraController), With<Camera>>,
|
||||
) {
|
||||
let dt = time.delta_seconds();
|
||||
|
||||
if let Ok((mut transform, mut options)) = query.get_single_mut() {
|
||||
if !options.initialized {
|
||||
let (yaw, pitch, _roll) = transform.rotation.to_euler(EulerRot::YXZ);
|
||||
options.yaw = yaw;
|
||||
options.pitch = pitch;
|
||||
options.initialized = true;
|
||||
}
|
||||
if !options.enabled {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle key input
|
||||
let mut axis_input = Vec3::ZERO;
|
||||
if key_input.pressed(options.key_forward) {
|
||||
axis_input.z += 1.0;
|
||||
}
|
||||
if key_input.pressed(options.key_back) {
|
||||
axis_input.z -= 1.0;
|
||||
}
|
||||
if key_input.pressed(options.key_right) {
|
||||
axis_input.x += 1.0;
|
||||
}
|
||||
if key_input.pressed(options.key_left) {
|
||||
axis_input.x -= 1.0;
|
||||
}
|
||||
if key_input.pressed(options.key_up) {
|
||||
axis_input.y += 1.0;
|
||||
}
|
||||
if key_input.pressed(options.key_down) {
|
||||
axis_input.y -= 1.0;
|
||||
}
|
||||
if key_input.just_pressed(options.keyboard_key_enable_mouse) {
|
||||
*move_toggled = !*move_toggled;
|
||||
}
|
||||
|
||||
// Apply movement update
|
||||
if axis_input != Vec3::ZERO {
|
||||
let max_speed = if key_input.pressed(options.key_run) {
|
||||
options.run_speed
|
||||
} else {
|
||||
options.walk_speed
|
||||
};
|
||||
options.velocity = axis_input.normalize() * max_speed;
|
||||
} else {
|
||||
let friction = options.friction.clamp(0.0, 1.0);
|
||||
options.velocity *= 1.0 - friction;
|
||||
if options.velocity.length_squared() < 1e-6 {
|
||||
options.velocity = Vec3::ZERO;
|
||||
}
|
||||
}
|
||||
let forward = transform.forward();
|
||||
let right = transform.right();
|
||||
transform.translation += options.velocity.x * dt * right
|
||||
+ options.velocity.y * dt * Vec3::Y
|
||||
+ options.velocity.z * dt * forward;
|
||||
|
||||
// Handle mouse input
|
||||
let mut mouse_delta = Vec2::ZERO;
|
||||
if mouse_button_input.pressed(options.mouse_key_enable_mouse) || *move_toggled {
|
||||
for mut window in &mut windows {
|
||||
if !window.focused {
|
||||
continue;
|
||||
}
|
||||
|
||||
window.cursor.grab_mode = CursorGrabMode::Locked;
|
||||
window.cursor.visible = false;
|
||||
}
|
||||
|
||||
for mouse_event in mouse_events.iter() {
|
||||
mouse_delta += mouse_event.delta;
|
||||
}
|
||||
}
|
||||
if mouse_button_input.just_released(options.mouse_key_enable_mouse) {
|
||||
for mut window in &mut windows {
|
||||
window.cursor.grab_mode = CursorGrabMode::None;
|
||||
window.cursor.visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
if mouse_delta != Vec2::ZERO {
|
||||
// Apply look update
|
||||
options.pitch = (options.pitch - mouse_delta.y * RADIANS_PER_DOT * options.sensitivity)
|
||||
.clamp(-PI / 2., PI / 2.);
|
||||
options.yaw -= mouse_delta.x * RADIANS_PER_DOT * options.sensitivity;
|
||||
transform.rotation = Quat::from_euler(EulerRot::ZYX, 0.0, options.yaw, options.pitch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct SnapToRenderCam;
|
||||
|
||||
pub(crate) fn snap_to_render_cam(mut viewport_query: Query<&mut Transform, (With<ViewportCam>, Without<RenderCam>)>, mut render_query: Query<&GlobalTransform, (With<RenderCam>, Without<ViewportCam>)>) {
|
||||
let view_tran: &mut Transform = &mut viewport_query.get_single_mut().unwrap();
|
||||
let rend_tran: &GlobalTransform = render_query.get_single_mut().unwrap();
|
||||
*view_tran = (*rend_tran).into();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
use bevy::{
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
use bevy_mod_picking::prelude::*;
|
||||
use bevy_transform_gizmo::GizmoTransformable;
|
||||
|
||||
use crate::Roko;
|
||||
|
||||
/// Add gizmo components to the entity with the ZST Roko marker struct
|
||||
pub(crate) fn gizmo_system(
|
||||
mut commands: Commands,
|
||||
query: Query<Entity, With<Roko>>,
|
||||
mut done: Local<bool>,
|
||||
)
|
||||
{
|
||||
if !*done {
|
||||
for entity in query.iter() {
|
||||
commands.entity(entity).insert(PickableBundle::default());
|
||||
commands.entity(entity).insert(GizmoTransformable);
|
||||
commands.entity(entity).insert(RaycastPickTarget::default());
|
||||
commands.entity(entity).insert(
|
||||
// When any mesh in the scene is selected, select the root entity's gizmo
|
||||
OnPointer::<Click>::send_event::<SelectRoot>(),
|
||||
);
|
||||
|
||||
*done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Makes everything in the scene with a mesh pickable
|
||||
pub(crate) fn make_pickable(
|
||||
mut commands: Commands,
|
||||
meshes: Query<Entity, (With<Handle<Mesh>>, Without<RaycastPickTarget>)>,
|
||||
) {
|
||||
for entity in meshes.iter() {
|
||||
commands.entity(entity).insert((
|
||||
PickableBundle::default(),
|
||||
RaycastPickTarget::default(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// When any mesh in the scene is selected, select Roko's gizmo
|
||||
pub(crate) struct SelectRoot(Entity);
|
||||
|
||||
impl From<ListenedEvent<Click>> for SelectRoot {
|
||||
fn from(event: ListenedEvent<Click>) -> Self {
|
||||
SelectRoot(event.listener)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// When any mesh in the scene is selected, select Roko's gizmo
|
||||
pub (crate) fn set_selection(mut events: EventReader<SelectRoot>, mut query: Query<(Entity, &mut PickSelection)> ) {
|
||||
for event in events.iter() {
|
||||
let (_entity, mut selection) = query.get_mut(event.0).unwrap();
|
||||
selection.is_selected = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
use bevy::{
|
||||
prelude::*,
|
||||
pbr::{CascadeShadowConfigBuilder, DirectionalLightShadowMap},
|
||||
};
|
||||
|
||||
pub(crate) fn light_setup(mut commands: Commands) {
|
||||
commands.insert_resource(AmbientLight {
|
||||
color: Color::YELLOW,
|
||||
brightness: 1.0 / 5.0f32,
|
||||
});
|
||||
commands.insert_resource(DirectionalLightShadowMap { size: 4096 });
|
||||
commands.spawn(DirectionalLightBundle {
|
||||
directional_light: DirectionalLight {
|
||||
shadows_enabled: true,
|
||||
..default()
|
||||
},
|
||||
cascade_shadow_config: CascadeShadowConfigBuilder {
|
||||
num_cascades: 1,
|
||||
maximum_distance: 1.6,
|
||||
..default()
|
||||
}
|
||||
.into(),
|
||||
transform: Transform {
|
||||
translation: Vec3::new(-5.0, 1.0, 5.0),
|
||||
rotation: Quat::from_xyzw(0.6, 0.0, -0.6, -0.5),
|
||||
scale: Vec3::ONE,
|
||||
},
|
||||
..default()
|
||||
});
|
||||
}
|
||||
@@ -1,518 +1,87 @@
|
||||
//! Loads and renders a glTF file as a scene.
|
||||
|
||||
use std::f32::consts::*;
|
||||
use core::time::Duration;
|
||||
|
||||
use bevy::{
|
||||
pbr::{CascadeShadowConfigBuilder, DirectionalLightShadowMap},
|
||||
prelude::*,
|
||||
scene::SceneInstance,
|
||||
animation::{AnimationClip, AnimationPlayer},
|
||||
prelude::*, window::{PresentMode, WindowMode},
|
||||
};
|
||||
|
||||
//use bevy_inspector_egui::quick::WorldInspectorPlugin;
|
||||
|
||||
mod gizmo;
|
||||
mod skybox;
|
||||
mod camera;
|
||||
mod animation;
|
||||
mod light;
|
||||
mod ui;
|
||||
|
||||
use bevy::{
|
||||
input::mouse::MouseMotion,
|
||||
};
|
||||
use bevy_mod_picking::{prelude::*, DefaultPickingPlugins};
|
||||
use bevy_transform_gizmo::TransformGizmoPlugin;
|
||||
use::bevy_egui::EguiPlugin;
|
||||
|
||||
use gizmo::{make_pickable, gizmo_system, SelectRoot, set_selection};
|
||||
use light::light_setup;
|
||||
use camera::{camera_setup, snap_to_render_cam, CameraControllerPlugin, SnapToRenderCam};
|
||||
use animation::{animation_setup, animation_system};
|
||||
use skybox::skybox_setup;
|
||||
use ui::ui_system;
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.insert_resource(AmbientLight {
|
||||
color: Color::WHITE,
|
||||
brightness: 1.0 / 5.0f32,
|
||||
})
|
||||
.insert_resource(DirectionalLightShadowMap { size: 4096 })
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_plugins(DefaultPlugins.set(WindowPlugin {
|
||||
primary_window: Some(Window {
|
||||
present_mode: PresentMode::AutoNoVsync, // Reduces input lag.
|
||||
fit_canvas_to_parent: true,
|
||||
//mode: WindowMode::Fullscreen,
|
||||
..default()
|
||||
}),
|
||||
..default()
|
||||
}))
|
||||
.add_plugin(CameraControllerPlugin)
|
||||
.add_plugins(DefaultPickingPlugins.build()
|
||||
.disable::<DebugPickingPlugin>()
|
||||
.disable::<DefaultHighlightingPlugin>()
|
||||
)
|
||||
.add_plugin(TransformGizmoPlugin::default())
|
||||
.add_plugin(EguiPlugin)
|
||||
//.add_plugin(WorldInspectorPlugin::new())
|
||||
.add_startup_system(setup)
|
||||
.add_system(setup_scene_once_loaded)
|
||||
//.add_system(animate_light_direction)
|
||||
//.add_system(animate_model_direction)
|
||||
.add_system(keyboard_animation_control)
|
||||
.add_system(camera_controller)
|
||||
.add_system(text_update_system)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
struct Animations(Vec<Handle<AnimationClip>>);
|
||||
|
||||
#[derive(Component)]
|
||||
struct PosText;
|
||||
|
||||
#[derive(Component)]
|
||||
struct RotText;
|
||||
|
||||
#[derive(Component)]
|
||||
struct MainCamera;
|
||||
|
||||
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
|
||||
|
||||
commands.insert_resource(Animations(vec![
|
||||
asset_server.load("Roko_Anim_Wave_noOptimization.glb#Animation0"),
|
||||
]));
|
||||
|
||||
|
||||
commands.spawn((
|
||||
Camera3dBundle {
|
||||
transform: Transform::from_xyz(0.0, 1.5, 1.5),
|
||||
//transform: Transform {
|
||||
//translation: Vec3::new(0.0, -10.0, 5.0),
|
||||
//rotation: Quat::from_xyzw(0.574, 0.0, 0.0, 0.819),
|
||||
//scale: Vec3::new(1.0, 1.0, 1.0),
|
||||
//},
|
||||
//projection: Projection::Orthographic(OrthographicProjection{
|
||||
//near: 0.1,
|
||||
//far: 100.0,
|
||||
//scale: 7.312,
|
||||
//..default()
|
||||
//}
|
||||
//),
|
||||
..default()
|
||||
},
|
||||
MainCamera,
|
||||
CameraController::default(),
|
||||
EnvironmentMapLight {
|
||||
diffuse_map: asset_server.load("pisa_diffuse_rgb9e5_zstd.ktx2"),
|
||||
specular_map: asset_server.load("pisa_specular_rgb9e5_zstd.ktx2"),
|
||||
},
|
||||
));
|
||||
|
||||
commands.spawn((
|
||||
TextBundle::from_sections([
|
||||
TextSection::new(
|
||||
"Position: ",
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 20.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
),
|
||||
TextSection::new(
|
||||
"X: ",
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 20.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
),
|
||||
TextSection::from_style(TextStyle {
|
||||
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
|
||||
font_size: 20.0,
|
||||
color: Color::GOLD,
|
||||
}),
|
||||
TextSection::new(
|
||||
"Y: ",
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 20.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
),
|
||||
TextSection::from_style(TextStyle {
|
||||
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
|
||||
font_size: 20.0,
|
||||
color: Color::GOLD,
|
||||
}),
|
||||
TextSection::new(
|
||||
"Z: ",
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 20.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
),
|
||||
TextSection::from_style(TextStyle {
|
||||
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
|
||||
font_size: 20.0,
|
||||
color: Color::GOLD,
|
||||
}),
|
||||
|
||||
]).with_style(
|
||||
Style {
|
||||
position_type: PositionType::Absolute,
|
||||
position: UiRect {
|
||||
right: Val::Px(00.0),
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
}
|
||||
),
|
||||
|
||||
PosText,
|
||||
));
|
||||
|
||||
commands.spawn((
|
||||
TextBundle::from_sections([
|
||||
TextSection::new(
|
||||
"Rotation: ",
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 20.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
),
|
||||
TextSection::new(
|
||||
"X: ",
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 20.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
),
|
||||
TextSection::from_style(TextStyle {
|
||||
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
|
||||
font_size: 20.0,
|
||||
color: Color::GOLD,
|
||||
}),
|
||||
TextSection::new(
|
||||
"Y: ",
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 20.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
),
|
||||
TextSection::from_style(TextStyle {
|
||||
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
|
||||
font_size: 20.0,
|
||||
color: Color::GOLD,
|
||||
}),
|
||||
TextSection::new(
|
||||
"Z: ",
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 20.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
),
|
||||
TextSection::from_style(TextStyle {
|
||||
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
|
||||
font_size: 20.0,
|
||||
color: Color::GOLD,
|
||||
}),
|
||||
TextSection::new(
|
||||
"W: ",
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 20.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
),
|
||||
TextSection::from_style(TextStyle {
|
||||
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
|
||||
font_size: 20.0,
|
||||
color: Color::GOLD,
|
||||
}),
|
||||
]).with_style(
|
||||
Style {
|
||||
position_type: PositionType::Absolute,
|
||||
position: UiRect {
|
||||
top: Val::Px(20.0),
|
||||
right: Val::Px(00.0),
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
}
|
||||
),
|
||||
RotText,
|
||||
));
|
||||
|
||||
|
||||
|
||||
commands.spawn(DirectionalLightBundle {
|
||||
directional_light: DirectionalLight {
|
||||
shadows_enabled: true,
|
||||
..default()
|
||||
},
|
||||
// This is a relatively small scene, so use tighter shadow
|
||||
// cascade bounds than the default for better quality.
|
||||
// We also adjusted the shadow map to be larger since we're
|
||||
// only using a single cascade.
|
||||
cascade_shadow_config: CascadeShadowConfigBuilder {
|
||||
num_cascades: 1,
|
||||
maximum_distance: 1.6,
|
||||
..default()
|
||||
}
|
||||
.into(),
|
||||
transform: Transform {
|
||||
translation: Vec3::new(0.0, 0.0, 0.0),
|
||||
rotation: Quat::from_xyzw(-0.5, 0.0, 0.0, 0.8517),
|
||||
scale: Vec3::ONE,
|
||||
},
|
||||
..default()
|
||||
});
|
||||
commands.spawn(SceneBundle {
|
||||
scene: asset_server.load("Roko_nogamerig.glb#Scene0"),
|
||||
transform: Transform::from_xyz(0.0, 0.0, 0.0),
|
||||
//transform: Transform::from_rotation(Quat::from_rotation_x(3.14)),
|
||||
..default()
|
||||
});
|
||||
|
||||
//commands.spawn(AnimationPlayer::default());
|
||||
|
||||
}
|
||||
|
||||
fn animate_light_direction(
|
||||
time: Res<Time>,
|
||||
mut query: Query<&mut Transform, With<DirectionalLight>>,
|
||||
) {
|
||||
for mut transform in &mut query {
|
||||
transform.rotation = Quat::from_euler(
|
||||
EulerRot::ZYX,
|
||||
0.0,
|
||||
1.0 * PI,
|
||||
-FRAC_PI_4,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn animate_model_direction(
|
||||
time: Res<Time>,
|
||||
mut query: Query<&mut Transform, With<SceneInstance>>,
|
||||
) {
|
||||
for mut transform in &mut query {
|
||||
transform.rotation = Quat::from_euler(
|
||||
EulerRot::XYZ,
|
||||
0.0,
|
||||
1.0 * PI,
|
||||
0.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Once the scene is loaded, start the animation
|
||||
fn setup_scene_once_loaded(
|
||||
animations: Res<Animations>,
|
||||
mut player: Query<&mut AnimationPlayer>,
|
||||
mut done: Local<bool>,
|
||||
) {
|
||||
if !*done {
|
||||
if let Ok(mut player) = player.get_single_mut() {
|
||||
player.play(animations.0[0].clone_weak()).repeat();
|
||||
*done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//fn play_animation(
|
||||
//mut animation_player: Query<&mut AnimationPlayer>,
|
||||
//animations: Res<Animations>,
|
||||
//mut done: Local<bool>,
|
||||
//time: Res<Time>,
|
||||
//assets: Res<Assets<AnimationClip>>,
|
||||
//) {
|
||||
//if !*done {
|
||||
//if let Ok(mut player) = animation_player.get_single_mut() {
|
||||
////println!("{:#?}", *assets.get(&animations.0[0]).unwrap().curves());
|
||||
//player.play(animations.0[0].clone_weak()).repeat();
|
||||
//*done = true;
|
||||
//} else {
|
||||
//println!("fail?!");
|
||||
//}
|
||||
//}
|
||||
|
||||
|
||||
fn keyboard_animation_control(
|
||||
keyboard_input: Res<Input<KeyCode>>,
|
||||
mut animation_player: Query<&mut AnimationPlayer>,
|
||||
animations: Res<Animations>,
|
||||
mut current_animation: Local<usize>,
|
||||
) {
|
||||
if let Ok(mut player) = animation_player.get_single_mut() {
|
||||
if keyboard_input.just_pressed(KeyCode::Space) {
|
||||
if player.is_paused() {
|
||||
player.resume();
|
||||
} else {
|
||||
player.pause();
|
||||
}
|
||||
}
|
||||
|
||||
if keyboard_input.just_pressed(KeyCode::Up) {
|
||||
let speed = player.speed();
|
||||
player.set_speed(speed * 1.2);
|
||||
}
|
||||
|
||||
if keyboard_input.just_pressed(KeyCode::Down) {
|
||||
let speed = player.speed();
|
||||
player.set_speed(speed * 0.8);
|
||||
}
|
||||
|
||||
if keyboard_input.just_pressed(KeyCode::Left) {
|
||||
let elapsed = player.elapsed();
|
||||
player.set_elapsed(elapsed - 0.1);
|
||||
}
|
||||
|
||||
if keyboard_input.just_pressed(KeyCode::Right) {
|
||||
let elapsed = player.elapsed();
|
||||
player.set_elapsed(elapsed + 0.1);
|
||||
}
|
||||
|
||||
if keyboard_input.just_pressed(KeyCode::Return) {
|
||||
*current_animation = (*current_animation + 1) % animations.0.len();
|
||||
player
|
||||
.play_with_transition(
|
||||
animations.0[*current_animation].clone_weak(),
|
||||
Duration::from_millis(250),
|
||||
)
|
||||
.repeat();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct CameraController {
|
||||
pub enabled: bool,
|
||||
pub initialized: bool,
|
||||
pub sensitivity: f32,
|
||||
pub key_forward: KeyCode,
|
||||
pub key_back: KeyCode,
|
||||
pub key_left: KeyCode,
|
||||
pub key_right: KeyCode,
|
||||
pub key_up: KeyCode,
|
||||
pub key_down: KeyCode,
|
||||
pub key_run: KeyCode,
|
||||
pub mouse_key_enable_mouse: MouseButton,
|
||||
pub keyboard_key_enable_mouse: KeyCode,
|
||||
pub walk_speed: f32,
|
||||
pub run_speed: f32,
|
||||
pub friction: f32,
|
||||
pub pitch: f32,
|
||||
pub yaw: f32,
|
||||
pub velocity: Vec3,
|
||||
}
|
||||
|
||||
impl Default for CameraController {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: true,
|
||||
initialized: false,
|
||||
sensitivity: 0.5,
|
||||
key_forward: KeyCode::W,
|
||||
key_back: KeyCode::S,
|
||||
key_left: KeyCode::A,
|
||||
key_right: KeyCode::D,
|
||||
key_up: KeyCode::E,
|
||||
key_down: KeyCode::Q,
|
||||
key_run: KeyCode::LShift,
|
||||
mouse_key_enable_mouse: MouseButton::Left,
|
||||
keyboard_key_enable_mouse: KeyCode::M,
|
||||
walk_speed: 2.0,
|
||||
run_speed: 6.0,
|
||||
friction: 0.5,
|
||||
pitch: 0.0,
|
||||
yaw: 0.0,
|
||||
velocity: Vec3::ZERO,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn camera_controller(
|
||||
time: Res<Time>,
|
||||
mut mouse_events: EventReader<MouseMotion>,
|
||||
mouse_button_input: Res<Input<MouseButton>>,
|
||||
key_input: Res<Input<KeyCode>>,
|
||||
mut move_toggled: Local<bool>,
|
||||
mut query: Query<(&mut Transform, &mut CameraController), With<Camera>>,
|
||||
) {
|
||||
let dt = time.delta_seconds();
|
||||
|
||||
if let Ok((mut transform, mut options)) = query.get_single_mut() {
|
||||
if !options.initialized {
|
||||
let (yaw, pitch, _roll) = transform.rotation.to_euler(EulerRot::YXZ);
|
||||
options.yaw = yaw;
|
||||
options.pitch = pitch;
|
||||
options.initialized = true;
|
||||
}
|
||||
if !options.enabled {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle key input
|
||||
let mut axis_input = Vec3::ZERO;
|
||||
if key_input.pressed(options.key_forward) {
|
||||
axis_input.z += 1.0;
|
||||
}
|
||||
if key_input.pressed(options.key_back) {
|
||||
axis_input.z -= 1.0;
|
||||
}
|
||||
if key_input.pressed(options.key_right) {
|
||||
axis_input.x += 1.0;
|
||||
}
|
||||
if key_input.pressed(options.key_left) {
|
||||
axis_input.x -= 1.0;
|
||||
}
|
||||
if key_input.pressed(options.key_up) {
|
||||
axis_input.y += 1.0;
|
||||
}
|
||||
if key_input.pressed(options.key_down) {
|
||||
axis_input.y -= 1.0;
|
||||
}
|
||||
if key_input.just_pressed(options.keyboard_key_enable_mouse) {
|
||||
*move_toggled = !*move_toggled;
|
||||
}
|
||||
|
||||
// Apply movement update
|
||||
if axis_input != Vec3::ZERO {
|
||||
let max_speed = if key_input.pressed(options.key_run) {
|
||||
options.run_speed
|
||||
} else {
|
||||
options.walk_speed
|
||||
};
|
||||
options.velocity = axis_input.normalize() * max_speed;
|
||||
} else {
|
||||
let friction = options.friction.clamp(0.0, 1.0);
|
||||
options.velocity *= 1.0 - friction;
|
||||
if options.velocity.length_squared() < 1e-6 {
|
||||
options.velocity = Vec3::ZERO;
|
||||
}
|
||||
}
|
||||
let forward = transform.forward();
|
||||
let right = transform.right();
|
||||
transform.translation += options.velocity.x * dt * right
|
||||
+ options.velocity.y * dt * Vec3::Y
|
||||
+ options.velocity.z * dt * forward;
|
||||
|
||||
// Handle mouse input
|
||||
let mut mouse_delta = Vec2::ZERO;
|
||||
if mouse_button_input.pressed(options.mouse_key_enable_mouse) || *move_toggled {
|
||||
for mouse_event in mouse_events.iter() {
|
||||
mouse_delta += mouse_event.delta;
|
||||
}
|
||||
}
|
||||
|
||||
if mouse_delta != Vec2::ZERO {
|
||||
// Apply look update
|
||||
options.pitch = (options.pitch - mouse_delta.y * 0.5 * options.sensitivity * dt)
|
||||
.clamp(-PI / 2., PI / 2.);
|
||||
options.yaw -= mouse_delta.x * options.sensitivity * dt;
|
||||
transform.rotation = Quat::from_euler(EulerRot::ZYX, 0.0, options.yaw, options.pitch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn text_update_system(camera_q: Query<(&Camera, &GlobalTransform), With<MainCamera>>,
|
||||
windows: Query<&mut Window>,
|
||||
mut pos_text: Query<&mut Text, With<PosText>>,
|
||||
mut rot_text: Query<&mut Text, Without<PosText>>
|
||||
) {
|
||||
let (camera, camera_transform) = camera_q.single();
|
||||
let window = windows.get_single().unwrap();
|
||||
let transform = camera_transform.compute_transform();
|
||||
|
||||
let position = transform.translation;
|
||||
let rotation = transform.rotation;
|
||||
for mut text in &mut pos_text {
|
||||
text.sections[2].value = format!("{:.2} ", position.x);
|
||||
text.sections[4].value = format!("{:.2} ", position.y);
|
||||
text.sections[6].value = format!("{:.2} ", position.z);
|
||||
}
|
||||
for mut text in &mut rot_text {
|
||||
text.sections[2].value = format!("{:.4} ", rotation.x);
|
||||
text.sections[4].value = format!("{:.4} ", rotation.y);
|
||||
text.sections[6].value = format!("{:.4} ", rotation.z);
|
||||
text.sections[8].value = format!("{:.4} ", rotation.w);
|
||||
}
|
||||
.add_startup_system(light_setup)
|
||||
.add_startup_system(skybox_setup)
|
||||
.add_startup_system(camera_setup)
|
||||
.add_startup_system(animation_setup)
|
||||
.add_system(gizmo_system)
|
||||
.add_system(animation_system)
|
||||
.add_system(make_pickable)
|
||||
.add_event::<SelectRoot>()
|
||||
.add_event::<SnapToRenderCam>()
|
||||
.add_system(set_selection.run_if(on_event::<SelectRoot>()))
|
||||
.add_system(ui_system)
|
||||
.add_system(snap_to_render_cam.run_if(on_event::<SnapToRenderCam>()))
|
||||
.run();
|
||||
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub(crate) struct Roko;
|
||||
|
||||
fn setup(mut meshes: ResMut<Assets<Mesh>>, mut materials: ResMut<Assets<StandardMaterial>>, mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
//roko scene
|
||||
let scene = asset_server.load("Roko_nogamerig.glb#Scene0");
|
||||
commands.spawn((SceneBundle {
|
||||
scene,
|
||||
..default()
|
||||
},
|
||||
Roko,
|
||||
));
|
||||
|
||||
//floor
|
||||
let floor = meshes.add(shape::Plane::from_size(10.0).into());
|
||||
let floor_mat = materials.add(StandardMaterial {
|
||||
base_color_texture: Some(asset_server.load("dirt_2_1024.png")),
|
||||
emissive_texture: Some(asset_server.load("dirt_2_1024.png")),
|
||||
unlit: true,
|
||||
..default()
|
||||
});
|
||||
commands.spawn(MaterialMeshBundle::<StandardMaterial> {
|
||||
mesh: floor,
|
||||
material: floor_mat,
|
||||
..default()
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
use std::f32::consts::TAU;
|
||||
|
||||
use bevy::{
|
||||
prelude::*,
|
||||
pbr::{NotShadowCaster, NotShadowReceiver},
|
||||
math::EulerRot::XYZ
|
||||
};
|
||||
|
||||
pub(crate) fn skybox_setup(mut meshes: ResMut<Assets<Mesh>>, mut materials: ResMut<Assets<StandardMaterial>>, mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
let spheremap_tex = asset_server.load("spheremap.png");
|
||||
let material = materials.add(StandardMaterial {
|
||||
base_color_texture: Some(spheremap_tex.clone()),
|
||||
emissive_texture: Some(spheremap_tex),
|
||||
double_sided:true,
|
||||
unlit: true,
|
||||
cull_mode: None,
|
||||
..default()
|
||||
});
|
||||
let sphere = meshes.add(shape::UVSphere {
|
||||
radius: 6.0,
|
||||
..default()
|
||||
}.into());
|
||||
|
||||
commands.spawn((MaterialMeshBundle::<StandardMaterial> {
|
||||
mesh: sphere,
|
||||
material,
|
||||
transform: Transform::from_rotation(Quat::from_euler(XYZ, -TAU / 4.0, 0.0, TAU / 2.0)).with_translation(Vec3::new(0.0, 3.0, 0.0)),
|
||||
..default()
|
||||
}, NotShadowCaster, NotShadowReceiver));
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
use bevy::prelude::*;
|
||||
use bevy_egui::{egui, EguiContexts};
|
||||
use crate::camera::SnapToRenderCam;
|
||||
|
||||
pub(crate) fn ui_system(mut ev: EventWriter::<SnapToRenderCam>, mut contexts: EguiContexts) {
|
||||
egui::Area::new("")
|
||||
.fixed_pos(egui::pos2(5.0, 5.0))
|
||||
.show(contexts.ctx_mut(), |ui| {
|
||||
if ui.button("Snap To Render Camera").clicked() {
|
||||
ev.send(SnapToRenderCam);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user