diff --git a/crosscom/ccidl/idl/radiance.idl b/crosscom/ccidl/idl/radiance.idl index 71b1a129..2102cf61 100644 --- a/crosscom/ccidl/idl/radiance.idl +++ b/crosscom/ccidl/idl/radiance.idl @@ -104,7 +104,10 @@ interface IEntity: IComponentContainer { class Entity: IEntity {} [uuid(8dd91852-476b-401b-8668-ba9cc331b7a1)] -interface IStaticMeshComponent: IComponent {} +interface IStaticMeshComponent: IComponent { + [internal(), rust()] + &'static radiance::components::mesh::static_mesh::StaticMeshComponent get(); +} [uuid(aa9cfbdc-59a2-4e9e-9280-f77d52e79494)] class StaticMeshComponent: IStaticMeshComponent {} diff --git a/radiance/radiance/src/comdef.rs b/radiance/radiance/src/comdef.rs index 117bfd86..a157b99f 100644 --- a/radiance/radiance/src/comdef.rs +++ b/radiance/radiance/src/comdef.rs @@ -1996,6 +1996,9 @@ pub struct IStaticMeshComponentVirtualTable { delta_sec: std::os::raw::c_float, ) -> (), pub on_unloading: unsafe extern "system" fn(this: *const *const std::os::raw::c_void) -> (), + pub get: fn( + this: *const *const std::os::raw::c_void, + ) -> &'static radiance::components::mesh::static_mesh::StaticMeshComponent, } #[repr(C)] @@ -2077,13 +2080,24 @@ impl IStaticMeshComponent { } } + pub fn get(&self) -> &'static radiance::components::mesh::static_mesh::StaticMeshComponent { + unsafe { + let this = self as *const IStaticMeshComponent as *const *const std::os::raw::c_void; + let ret = ((*self.vtable).get)(this); + + ret + } + } + pub fn uuid() -> uuid::Uuid { use crosscom::ComInterface; uuid::Uuid::from_bytes(IStaticMeshComponent::INTERFACE_ID) } } -pub trait IStaticMeshComponentImpl {} +pub trait IStaticMeshComponentImpl { + fn get(&self) -> &'static radiance::components::mesh::static_mesh::StaticMeshComponent; +} impl crosscom::ComInterface for IStaticMeshComponent { // 8dd91852-476b-401b-8668-ba9cc331b7a1 @@ -2184,6 +2198,15 @@ macro_rules! ComObject_StaticMeshComponent { (previous - 1) as std::os::raw::c_long } + fn get( + this: *const *const std::os::raw::c_void, + ) -> &'static radiance::components::mesh::static_mesh::StaticMeshComponent { + unsafe { + let __crosscom_object = crosscom::get_object::(this); + (*__crosscom_object).inner.get() + } + } + unsafe extern "system" fn on_loading(this: *const *const std::os::raw::c_void) -> () { let __crosscom_object = crosscom::get_object::(this); (*__crosscom_object).inner.on_loading().into() @@ -2219,6 +2242,7 @@ macro_rules! ComObject_StaticMeshComponent { on_loading, on_updating, on_unloading, + get, }, }; diff --git a/radiance/radiance/src/components/mesh/static_mesh.rs b/radiance/radiance/src/components/mesh/static_mesh.rs index c83150e4..e6f8087f 100644 --- a/radiance/radiance/src/components/mesh/static_mesh.rs +++ b/radiance/radiance/src/components/mesh/static_mesh.rs @@ -3,7 +3,7 @@ use std::rc::Rc; use crosscom::ComRc; use crate::{ - comdef::{IComponentImpl, IEntity}, + comdef::{IComponentImpl, IEntity, IStaticMeshComponent, IStaticMeshComponentImpl}, rendering::ComponentFactory, ComObject_StaticMeshComponent, }; @@ -30,6 +30,16 @@ impl StaticMeshComponent { component_factory, } } + + pub fn get_geometries(&self) -> &[Geometry] { + &self.geometries + } +} + +impl IStaticMeshComponentImpl for StaticMeshComponent { + fn get(&self) -> &'static StaticMeshComponent { + unsafe { &*(self as *const _) } + } } impl IComponentImpl for StaticMeshComponent { diff --git a/radiance/radiance/src/math/vec.rs b/radiance/radiance/src/math/vec.rs index e6da4332..09b020cc 100644 --- a/radiance/radiance/src/math/vec.rs +++ b/radiance/radiance/src/math/vec.rs @@ -118,10 +118,14 @@ impl Vec3 { Vec3::new(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z) } - pub fn dot(lhs: f32, rhs: &Vec3) -> Self { + pub fn scalar_mul(lhs: f32, rhs: &Vec3) -> Self { Vec3::new(lhs * rhs.x, lhs * rhs.y, lhs * rhs.z) } + pub fn dot(lhs: &Vec3, rhs: &Vec3) -> f32 { + lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z + } + pub fn cross(lhs: &Vec3, rhs: &Vec3) -> Self { Vec3::new( lhs.y * rhs.z - lhs.z * rhs.y, diff --git a/radiance/radiance/src/rendering/vertex_buffer.rs b/radiance/radiance/src/rendering/vertex_buffer.rs index 941eef43..e25b5a6c 100644 --- a/radiance/radiance/src/rendering/vertex_buffer.rs +++ b/radiance/radiance/src/rendering/vertex_buffer.rs @@ -288,4 +288,21 @@ impl VertexBuffer { pub fn components(&self) -> VertexComponents { self.layout.components } + + pub fn to_position_vec(&self) -> Vec { + let vertex_size = self.layout.size; + let mut result = Vec::with_capacity(self.count); + let offset = self.layout.get_offset(VertexComponents::POSITION).unwrap(); + for i in 0..self.count { + let data: &Vec3 = unsafe { + &*(self + .data + .as_ptr() + .offset((i * vertex_size + offset) as isize) as *const Vec3) + }; + result.push(*data); + } + + result + } } diff --git a/radiance/radiance/src/rendering/vitagl/shaders/simple_triangle.frag b/radiance/radiance/src/rendering/vitagl/shaders/simple_triangle.frag index 9c4841d9..15848d75 100644 --- a/radiance/radiance/src/rendering/vitagl/shaders/simple_triangle.frag +++ b/radiance/radiance/src/rendering/vitagl/shaders/simple_triangle.frag @@ -4,9 +4,11 @@ float4 main( float2 texcoord : TEXCOORD0 ) { float4 color = tex2D(texSampler, texcoord); - if (color.a < 0.9) { + if (color.a < 0.4) { discard; - } + } else { + color.a = 1.0; + } return color; } diff --git a/radiance/radiance/src/rendering/vulkan/shaders/simple_triangle.frag b/radiance/radiance/src/rendering/vulkan/shaders/simple_triangle.frag index 0d57ffd5..7437801c 100644 --- a/radiance/radiance/src/rendering/vulkan/shaders/simple_triangle.frag +++ b/radiance/radiance/src/rendering/vulkan/shaders/simple_triangle.frag @@ -10,5 +10,7 @@ void main() { outColor = texture(texSampler, fragTexCoord); if (outColor.a < 0.4) { discard; + } else { + outColor.a = 1.0; } -} \ No newline at end of file +} diff --git a/radiance/radiance/src/utils/free_view.rs b/radiance/radiance/src/utils/free_view.rs index f0b6d5a5..cada6a03 100644 --- a/radiance/radiance/src/utils/free_view.rs +++ b/radiance/radiance/src/utils/free_view.rs @@ -45,7 +45,7 @@ impl FreeViewController { if movement.norm() > 0.0 { movement.normalize(); - movement = Vec3::dot(SPEED * delta_sec, &movement); + movement = Vec3::scalar_mul(SPEED * delta_sec, &movement); transform.translate_local(&movement); } @@ -62,7 +62,7 @@ impl FreeViewController { if movement.norm() > 0.0 { movement.normalize(); - movement = Vec3::dot(SPEED * delta_sec, &movement); + movement = Vec3::scalar_mul(SPEED * delta_sec, &movement); transform.translate(&movement); } diff --git a/radiance/radiance/src/utils/mod.rs b/radiance/radiance/src/utils/mod.rs index 9d1cd1c8..15074fe9 100644 --- a/radiance/radiance/src/utils/mod.rs +++ b/radiance/radiance/src/utils/mod.rs @@ -1,6 +1,7 @@ pub mod act_drop; pub mod free_view; pub mod interp_value; +pub mod ray_casting; use std::io::{Read, Seek}; diff --git a/radiance/radiance/src/utils/ray_casting/mesh.rs b/radiance/radiance/src/utils/ray_casting/mesh.rs new file mode 100644 index 00000000..c62b4b7a --- /dev/null +++ b/radiance/radiance/src/utils/ray_casting/mesh.rs @@ -0,0 +1,178 @@ +use crate::math::Vec3; + +use super::AARayDirection; + +struct Triangle { + indices: [u32; 3], + aabb_min: Vec3, + aabb_max: Vec3, +} + +impl Triangle { + pub fn new(indices: [u32; 3], vertices: &Vec) -> Self { + let a = vertices[indices[0] as usize]; + let b = vertices[indices[1] as usize]; + let c = vertices[indices[2] as usize]; + let aabb_min = Vec3::new( + a.x.min(b.x).min(c.x), + a.y.min(b.y).min(c.y), + a.z.min(b.z).min(c.z), + ); + let aabb_max = Vec3::new( + a.x.max(b.x).max(c.x), + a.y.max(b.y).max(c.y), + a.z.max(b.z).max(c.z), + ); + + Self { + indices, + aabb_min, + aabb_max, + } + } + + pub fn cast_aaray( + &self, + ray_origin: Vec3, + aaray: AARayDirection, + vertices: &Vec, + ) -> Option { + let aabb_min = self.aabb_min; + let aabb_max = self.aabb_max; + let ray_direction = aaray.get_direction(); + + if ray_direction.x != 0. { + if ray_origin.y < aabb_min.y + || ray_origin.y > aabb_max.y + || ray_origin.z < aabb_min.z + || ray_origin.z > aabb_max.z + { + return None; + } + } else if ray_direction.y != 0. { + if ray_origin.x < aabb_min.x + || ray_origin.x > aabb_max.x + || ray_origin.z < aabb_min.z + || ray_origin.z > aabb_max.z + { + return None; + } + } else if ray_direction.z != 0. { + if ray_origin.x < aabb_min.x + || ray_origin.x > aabb_max.x + || ray_origin.y < aabb_min.y + || ray_origin.y > aabb_max.y + { + return None; + } + } + + self.cast_ray(ray_origin, ray_direction, vertices) + } + + pub fn cast_ray( + &self, + ray_origin: Vec3, + ray_direction: Vec3, + vertices: &Vec, + ) -> Option { + const EPSILON: f32 = 0.00001; + let edge1 = Vec3::sub( + &vertices[self.indices[1] as usize], + &vertices[self.indices[0] as usize], + ); + let edge2 = Vec3::sub( + &vertices[self.indices[2] as usize], + &vertices[self.indices[0] as usize], + ); + + let h = Vec3::cross(&ray_direction, &edge2); + let a = Vec3::dot(&edge1, &h); + + if a > -EPSILON && a < EPSILON { + // println!("none1"); + return None; + } + + let f = 1.0 / a; + let s = Vec3::sub(&ray_origin, &vertices[self.indices[0] as usize]); + let u = f * Vec3::dot(&s, &h); + if u < 0.0 || u > 1.0 { + // println!("none2"); + return None; + } + + let q = Vec3::cross(&s, &edge1); + let v = f * Vec3::dot(&ray_direction, &q); + + if v < 0.0 || u + v > 1.0 { + // println!("none3"); + return None; + } + + let t = f * Vec3::dot(&edge2, &q); + + if t > EPSILON { + Some(t) + } else { + // println!("none4 t: {}", t); + None + } + } +} + +pub(crate) struct Mesh { + vertices: Vec, + triangles: Vec, +} + +impl Mesh { + pub fn new(vertices: Vec, indices: Vec) -> Self { + let mut triangles = Vec::new(); + for i in (0..indices.len()).step_by(3) { + triangles.push(Triangle::new( + [indices[i], indices[i + 1], indices[i + 2]], + &vertices, + )); + } + + Self { + vertices, + triangles, + } + } + + pub fn cast_aaray(&self, ray_origin: Vec3, aaray: AARayDirection) -> Option { + let mut min_distance = None; + for triangle in &self.triangles { + if let Some(distance) = triangle.cast_aaray(ray_origin, aaray, &self.vertices) { + if let Some(md) = min_distance { + if distance < md { + min_distance = Some(distance); + } + } else { + min_distance = Some(distance); + } + } + } + + min_distance + } + + pub fn cast_ray(&self, ray_origin: Vec3, ray_direction: Vec3) -> Option { + let mut min_distance = None; + for triangle in &self.triangles { + if let Some(distance) = triangle.cast_ray(ray_origin, ray_direction, &self.vertices) { + if let Some(md) = min_distance { + if distance < md { + min_distance = Some(distance); + } + } else { + min_distance = Some(distance); + } + } + } + + min_distance + } +} diff --git a/radiance/radiance/src/utils/ray_casting/mod.rs b/radiance/radiance/src/utils/ray_casting/mod.rs new file mode 100644 index 00000000..59ac777b --- /dev/null +++ b/radiance/radiance/src/utils/ray_casting/mod.rs @@ -0,0 +1,82 @@ +use crate::math::Vec3; + +mod mesh; + +pub struct RayCaster { + colliders: Vec, +} + +impl RayCaster { + pub fn new() -> Self { + Self { + colliders: Vec::new(), + } + } + + pub fn add_mesh(&mut self, vertices: Vec, indices: Vec) { + self.colliders.push(mesh::Mesh::new(vertices, indices)); + } + + pub fn cast_aaray(&self, ray_origin: Vec3, ray_direction: AARayDirection) -> Option { + let mut min_distance = f32::MAX; + let mut hit = false; + + for collider in &self.colliders { + if let Some(distance) = collider.cast_aaray(ray_origin, ray_direction) { + if distance < min_distance { + min_distance = distance; + hit = true; + } + } + } + + if hit { + Some(min_distance) + } else { + None + } + } + + pub fn cast_ray(&self, ray_origin: Vec3, ray_direction: Vec3) -> Option { + let mut min_distance = f32::MAX; + let mut hit = false; + + for collider in &self.colliders { + if let Some(distance) = collider.cast_ray(ray_origin, ray_direction) { + if distance < min_distance { + min_distance = distance; + hit = true; + } + } + } + + if hit { + Some(min_distance) + } else { + None + } + } +} + +#[derive(Clone, Copy)] +pub enum AARayDirection { + X, + Y, + Z, + NX, + NY, + NZ, +} + +impl AARayDirection { + pub fn get_direction(&self) -> Vec3 { + match self { + AARayDirection::X => Vec3::new(1., 0., 0.), + AARayDirection::Y => Vec3::new(0., 1., 0.), + AARayDirection::Z => Vec3::new(0., 0., 1.), + AARayDirection::NX => Vec3::new(-1., 0., 0.), + AARayDirection::NY => Vec3::new(0., -1., 0.), + AARayDirection::NZ => Vec3::new(0., 0., -1.), + } + } +} diff --git a/radiance/radiance_editor/src/comdef.rs b/radiance/radiance_editor/src/comdef.rs index 38991590..279a4cc8 100644 --- a/radiance/radiance_editor/src/comdef.rs +++ b/radiance/radiance_editor/src/comdef.rs @@ -1,3 +1,4 @@ +use crate as radiance_editor; // Interface IViewContent #[repr(C)] diff --git a/yaobow/shared/src/openpal3/directors/adv_director.rs b/yaobow/shared/src/openpal3/directors/adv_director.rs index 4c07a901..0b31153b 100644 --- a/yaobow/shared/src/openpal3/directors/adv_director.rs +++ b/yaobow/shared/src/openpal3/directors/adv_director.rs @@ -214,8 +214,10 @@ impl AdventureDirectorProps { let scene = scene_manager.scn_scene().unwrap().get(); let speed = 175.; - let mut target_position = - Vec3::add(&position, &Vec3::dot(speed * delta_sec, &moving_direction)); + let mut target_position = Vec3::add( + &position, + &Vec3::scalar_mul(speed * delta_sec, &moving_direction), + ); let target_nav_coord = scene.scene_coord_to_nav_coord(role_controller.get().nav_layer(), &target_position); let height = scene.get_height(role_controller.get().nav_layer(), target_nav_coord); diff --git a/yaobow/shared/src/openpal4/actor.rs b/yaobow/shared/src/openpal4/actor.rs index ba28d773..13cab189 100644 --- a/yaobow/shared/src/openpal4/actor.rs +++ b/yaobow/shared/src/openpal4/actor.rs @@ -12,6 +12,7 @@ use radiance::{ }, input::InputEngine, math::{Mat44, Vec3}, + utils::ray_casting::RayCaster, }; use crate::{ @@ -106,6 +107,7 @@ struct Pal4ActorControllerInner { input: Rc>, entity: ComRc, scene: ComRc, + ray_caster: RayCaster, lock_control: bool, camera_rotation: f32, } @@ -115,11 +117,13 @@ impl Pal4ActorControllerInner { input: Rc>, entity: ComRc, scene: ComRc, + ray_caster: RayCaster, ) -> Self { Self { input, entity, scene, + ray_caster, lock_control: false, camera_rotation: 0., } @@ -134,16 +138,36 @@ impl Pal4ActorControllerInner { let current_position = self.entity.transform().borrow().position(); let direction = get_moving_direction(self.input.clone(), self.scene.clone()); - let target_position = - Vec3::add(¤t_position, &Vec3::dot(speed * delta_sec, &direction)); + let target_position = Vec3::add( + ¤t_position, + &Vec3::scalar_mul(speed * delta_sec, &direction), + ); + + const STEP_HEIGHT: f32 = 10.; - if direction.norm() > 0.5 { - let look_at = Vec3::new(current_position.x, target_position.y, current_position.z); - self.entity - .transform() - .borrow_mut() - .set_position(&target_position) - .look_at(&look_at); + let ray_origin = Vec3::new( + target_position.x, + target_position.y + STEP_HEIGHT, + target_position.z, + ); + let p = self + .ray_caster + .cast_aaray(ray_origin, radiance::utils::ray_casting::AARayDirection::NY); + + if let Some(p) = p { + if direction.norm() > 0.5 { + let target_position = Vec3::new( + target_position.x, + target_position.y + STEP_HEIGHT - p, + target_position.z, + ); + let look_at = Vec3::new(current_position.x, target_position.y, current_position.z); + self.entity + .transform() + .borrow_mut() + .set_position(&target_position) + .look_at(&look_at); + } } self.camera_rotation = @@ -170,9 +194,12 @@ impl Pal4ActorController { input: Rc>, entity: ComRc, scene: ComRc, + ray_caster: RayCaster, ) -> Pal4ActorController { Self { - inner: RefCell::new(Pal4ActorControllerInner::new(input, entity, scene)), + inner: RefCell::new(Pal4ActorControllerInner::new( + input, entity, scene, ray_caster, + )), } } } diff --git a/yaobow/shared/src/openpal4/asset_loader.rs b/yaobow/shared/src/openpal4/asset_loader.rs index 08ceb0b1..f301d027 100644 --- a/yaobow/shared/src/openpal4/asset_loader.rs +++ b/yaobow/shared/src/openpal4/asset_loader.rs @@ -151,7 +151,7 @@ impl AssetLoader { scene_name, block_name, block_name, ); - self.try_load_scene_dff(path) + self.try_load_dff(path, "sky".to_string()) } pub fn try_load_scene_clip( @@ -164,7 +164,7 @@ impl AssetLoader { scene_name, block_name, block_name, ); - self.try_load_scene_dff(path) + self.try_load_dff(path, "clip".to_string()) } pub fn try_load_scene_clip_na( @@ -177,16 +177,16 @@ impl AssetLoader { scene_name, block_name, block_name, ); - self.try_load_scene_dff(path) + self.try_load_dff(path, "clipNA".to_string()) } - fn try_load_scene_dff(&self, path: String) -> Option> { + fn try_load_dff(&self, path: String, object_name: String) -> Option> { if self.vfs.exists(&path) { let entity = create_entity_from_dff_model( &self.component_factory, &self.vfs, path.clone(), - "world_clip".to_string(), + object_name, true, &DffLoaderConfig { texture_resolver: &self.texture_resolver, @@ -202,6 +202,24 @@ impl AssetLoader { } } + pub fn load_scene_floor(&self, scene_name: &str, block_name: &str) -> Option> { + let path = format!( + "/gamedata/scenedata/{}/{}/{}_floor.dff", + scene_name, block_name, block_name, + ); + + self.try_load_dff(path, "floor".to_string()) + } + + pub fn load_scene_wall(&self, scene_name: &str, block_name: &str) -> Option> { + let path = format!( + "/gamedata/scenedata/{}/{}/{}_wall.dff", + scene_name, block_name, block_name, + ); + + self.try_load_dff(path, "wall".to_string()) + } + pub fn load_npc_info(&self, scene_name: &str, block_name: &str) -> anyhow::Result { let path = format!( "/gamedata/scenedata/{}/{}/npcInfo.npc", diff --git a/yaobow/shared/src/openpal4/scene.rs b/yaobow/shared/src/openpal4/scene.rs index 3d5e5440..9e5963ec 100644 --- a/yaobow/shared/src/openpal4/scene.rs +++ b/yaobow/shared/src/openpal4/scene.rs @@ -1,11 +1,13 @@ use std::{cell::RefCell, rc::Rc}; use crosscom::ComRc; +use itertools::Itertools; use radiance::{ - comdef::{IEntity, IScene}, + comdef::{IEntity, IScene, IStaticMeshComponent}, input::InputEngine, - math::Vec3, + math::{Transform, Vec3}, scene::{CoreEntity, CoreScene}, + utils::ray_casting::RayCaster, }; use super::{ @@ -90,6 +92,19 @@ impl Pal4Scene { scene.camera().borrow_mut().set_fov43(45_f32.to_radians()); + let mut ray_caster = RayCaster::new(); + let floor = asset_loader.load_scene_floor(scene_name, block_name); + let wall = asset_loader.load_scene_wall(scene_name, block_name); + setup_ray_caster(&mut ray_caster, floor.clone(), wall.clone()); + + /*if let Some(floor) = floor { + scene.add_entity(floor); + } + + if let Some(wall) = wall { + scene.add_entity(wall); + }*/ + let players = [ load_player(asset_loader, Player::YunTianhe), load_player(asset_loader, Player::HanLingsha), @@ -97,7 +112,8 @@ impl Pal4Scene { load_player(asset_loader, Player::MurongZiying), ]; - let controller = Pal4ActorController::create(input, players[0].clone(), scene.clone()); + let controller = + Pal4ActorController::create(input, players[0].clone(), scene.clone(), ray_caster); players[0].add_component(IPal4ActorController::uuid(), ComRc::from_object(controller)); for p in &players { @@ -180,3 +196,45 @@ fn load_player(asset_loader: &AssetLoader, player: Player) -> ComRc { entity } + +fn setup_ray_caster( + ray_caster: &mut RayCaster, + floor: Option>, + wall: Option>, +) { + if let Some(floor) = floor { + floor.update_world_transform(&Transform::new()); + add_mesh(ray_caster, floor); + } + + if let Some(wall) = wall { + wall.update_world_transform(&Transform::new()); + add_mesh(ray_caster, wall); + } +} + +fn add_mesh(ray_caster: &mut RayCaster, entity: ComRc) { + for child in entity.children() { + add_mesh(ray_caster, child); + } + + let mesh = entity.get_component(IStaticMeshComponent::uuid()); + if let Some(mesh) = mesh { + let mesh = mesh.query_interface::().unwrap(); + let mesh = mesh.get(); + let geometries = mesh.get_geometries(); + let entity_position = entity.world_transform().position(); + + for geometry in geometries { + let v = geometry + .vertices + .to_position_vec() + .into_iter() + .map(|v| Vec3::add(&entity_position, &v)) + .collect(); + + let i = geometry.indices.clone(); + ray_caster.add_mesh(v, i); + } + } +} diff --git a/yaobow/shared/src/scripting/sce/commands/role_move_to.rs b/yaobow/shared/src/scripting/sce/commands/role_move_to.rs index ffcf4357..368d9101 100644 --- a/yaobow/shared/src/scripting/sce/commands/role_move_to.rs +++ b/yaobow/shared/src/scripting/sce/commands/role_move_to.rs @@ -59,7 +59,10 @@ impl SceCommand for SceCommandRoleMoveTo { let new_position = if completed { to } else { - Vec3::add(&position, &Vec3::dot(step, &Vec3::normalized(&remain))) + Vec3::add( + &position, + &Vec3::scalar_mul(step, &Vec3::normalized(&remain)), + ) }; role.transform() diff --git a/yaobow/shared/src/scripting/sce/commands/role_path_to.rs b/yaobow/shared/src/scripting/sce/commands/role_path_to.rs index 4bea37d4..3d6d543a 100644 --- a/yaobow/shared/src/scripting/sce/commands/role_path_to.rs +++ b/yaobow/shared/src/scripting/sce/commands/role_path_to.rs @@ -69,7 +69,10 @@ impl SceCommand for SceCommandRolePathTo { let new_position = if completed { to } else { - Vec3::add(&position, &Vec3::dot(step, &Vec3::normalized(&remain))) + Vec3::add( + &position, + &Vec3::scalar_mul(step, &Vec3::normalized(&remain)), + ) }; let mut look_at = Vec3::new(to.x, position.y, to.z);