use crate::common::{C2Vector, C3Vector, Quaternion};
use glam::Vec3;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CoordinateSystem {
Blender,
Unity,
UnrealEngine,
}
pub fn transform_position(wow_pos: C3Vector, target: CoordinateSystem) -> C3Vector {
match target {
CoordinateSystem::Blender => C3Vector {
x: wow_pos.y, y: -wow_pos.x, z: wow_pos.z, },
CoordinateSystem::Unity => C3Vector {
x: -wow_pos.y, y: wow_pos.z, z: wow_pos.x, },
CoordinateSystem::UnrealEngine => C3Vector {
x: wow_pos.x, y: -wow_pos.y, z: wow_pos.z, },
}
}
pub fn transform_quaternion(wow_quat: Quaternion, target: CoordinateSystem) -> Quaternion {
match target {
CoordinateSystem::Blender => Quaternion {
x: wow_quat.y, y: -wow_quat.x, z: wow_quat.z, w: wow_quat.w, },
CoordinateSystem::Unity => Quaternion {
x: wow_quat.y, y: -wow_quat.z, z: -wow_quat.x, w: wow_quat.w, },
CoordinateSystem::UnrealEngine => Quaternion {
x: -wow_quat.x, y: wow_quat.y, z: -wow_quat.z, w: wow_quat.w, },
}
}
pub fn transform_vector2(wow_vec: C2Vector, _target: CoordinateSystem) -> C2Vector {
wow_vec
}
pub struct CoordinateTransformer {
target: CoordinateSystem,
}
impl CoordinateTransformer {
pub fn new(target: CoordinateSystem) -> Self {
Self { target }
}
pub fn transform_position(&self, wow_pos: C3Vector) -> C3Vector {
transform_position(wow_pos, self.target)
}
pub fn transform_quaternion(&self, wow_quat: Quaternion) -> Quaternion {
transform_quaternion(wow_quat, self.target)
}
pub fn transform_positions(&self, positions: &[C3Vector]) -> Vec<C3Vector> {
positions
.iter()
.map(|&pos| self.transform_position(pos))
.collect()
}
pub fn transform_quaternions(&self, quaternions: &[Quaternion]) -> Vec<Quaternion> {
quaternions
.iter()
.map(|&quat| self.transform_quaternion(quat))
.collect()
}
pub fn transform_positions_simd(&self, positions: &[C3Vector]) -> Vec<C3Vector> {
positions
.iter()
.map(|pos| {
let glam_pos = pos.to_glam();
let transformed = self.transform_glam_position(glam_pos);
C3Vector::from_glam(transformed)
})
.collect()
}
fn transform_glam_position(&self, wow_pos: Vec3) -> Vec3 {
match self.target {
CoordinateSystem::Blender => Vec3::new(wow_pos.y, -wow_pos.x, wow_pos.z),
CoordinateSystem::Unity => Vec3::new(-wow_pos.y, wow_pos.z, wow_pos.x),
CoordinateSystem::UnrealEngine => Vec3::new(wow_pos.x, -wow_pos.y, wow_pos.z),
}
}
}
pub mod matrix {
use super::CoordinateSystem;
use glam::{Mat4, Vec3};
pub fn get_transform_matrix(target: CoordinateSystem) -> Mat4 {
match target {
CoordinateSystem::Blender => Mat4::from_cols(
Vec3::new(0.0, 1.0, 0.0).extend(0.0), Vec3::new(-1.0, 0.0, 0.0).extend(0.0), Vec3::new(0.0, 0.0, 1.0).extend(0.0), Vec3::new(0.0, 0.0, 0.0).extend(1.0), ),
CoordinateSystem::Unity => Mat4::from_cols(
Vec3::new(0.0, -1.0, 0.0).extend(0.0), Vec3::new(0.0, 0.0, 1.0).extend(0.0), Vec3::new(1.0, 0.0, 0.0).extend(0.0), Vec3::new(0.0, 0.0, 0.0).extend(1.0), ),
CoordinateSystem::UnrealEngine => Mat4::from_cols(
Vec3::new(1.0, 0.0, 0.0).extend(0.0), Vec3::new(0.0, -1.0, 0.0).extend(0.0), Vec3::new(0.0, 0.0, 1.0).extend(0.0), Vec3::new(0.0, 0.0, 0.0).extend(1.0), ),
}
}
pub fn transform_matrix(wow_matrix: Mat4, target: CoordinateSystem) -> Mat4 {
let transform = get_transform_matrix(target);
let inverse_transform = transform.transpose();
transform * wow_matrix * inverse_transform
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_blender_position_transform() {
let wow_pos = C3Vector {
x: 100.0,
y: 200.0,
z: 50.0,
};
let blender_pos = transform_position(wow_pos, CoordinateSystem::Blender);
assert_eq!(blender_pos.x, 200.0);
assert_eq!(blender_pos.y, -100.0);
assert_eq!(blender_pos.z, 50.0);
}
#[test]
fn test_unity_position_transform() {
let wow_pos = C3Vector {
x: 100.0,
y: 200.0,
z: 50.0,
};
let unity_pos = transform_position(wow_pos, CoordinateSystem::Unity);
assert_eq!(unity_pos.x, -200.0);
assert_eq!(unity_pos.y, 50.0);
assert_eq!(unity_pos.z, 100.0);
}
#[test]
fn test_unreal_position_transform() {
let wow_pos = C3Vector {
x: 100.0,
y: 200.0,
z: 50.0,
};
let unreal_pos = transform_position(wow_pos, CoordinateSystem::UnrealEngine);
assert_eq!(unreal_pos.x, 100.0);
assert_eq!(unreal_pos.y, -200.0);
assert_eq!(unreal_pos.z, 50.0);
}
#[test]
fn test_identity_quaternion_transforms() {
let identity = Quaternion {
x: 0.0,
y: 0.0,
z: 0.0,
w: 1.0,
};
let blender_quat = transform_quaternion(identity, CoordinateSystem::Blender);
let unity_quat = transform_quaternion(identity, CoordinateSystem::Unity);
let unreal_quat = transform_quaternion(identity, CoordinateSystem::UnrealEngine);
assert_eq!(blender_quat, identity);
assert_eq!(unity_quat, identity);
assert_eq!(unreal_quat, identity);
}
#[test]
fn test_coordinate_transformer() {
let transformer = CoordinateTransformer::new(CoordinateSystem::Blender);
let positions = vec![
C3Vector {
x: 0.0,
y: 0.0,
z: 0.0,
},
C3Vector {
x: 100.0,
y: 200.0,
z: 50.0,
},
];
let transformed = transformer.transform_positions(&positions);
assert_eq!(
transformed[0],
C3Vector {
x: 0.0,
y: 0.0,
z: 0.0
}
);
assert_eq!(
transformed[1],
C3Vector {
x: 200.0,
y: -100.0,
z: 50.0
}
);
}
#[test]
fn test_simd_transform_consistency() {
let transformer = CoordinateTransformer::new(CoordinateSystem::Blender);
let positions = vec![
C3Vector {
x: 100.0,
y: 200.0,
z: 50.0,
},
C3Vector {
x: -50.0,
y: 75.0,
z: 25.0,
},
];
let regular_transform = transformer.transform_positions(&positions);
let simd_transform = transformer.transform_positions_simd(&positions);
for (regular, simd) in regular_transform.iter().zip(simd_transform.iter()) {
assert!((regular.x - simd.x).abs() < f32::EPSILON);
assert!((regular.y - simd.y).abs() < f32::EPSILON);
assert!((regular.z - simd.z).abs() < f32::EPSILON);
}
}
#[test]
fn test_transform_matrix_construction() {
use crate::coordinate::matrix::get_transform_matrix;
let blender_matrix = get_transform_matrix(CoordinateSystem::Blender);
let unity_matrix = get_transform_matrix(CoordinateSystem::Unity);
let unreal_matrix = get_transform_matrix(CoordinateSystem::UnrealEngine);
assert!(blender_matrix.determinant().abs() > f32::EPSILON);
assert!(unity_matrix.determinant().abs() > f32::EPSILON);
assert!(unreal_matrix.determinant().abs() > f32::EPSILON);
}
#[test]
fn test_cardinal_directions() {
let north = C3Vector {
x: 1.0,
y: 0.0,
z: 0.0,
};
let blender_north = transform_position(north, CoordinateSystem::Blender);
assert_eq!(
blender_north,
C3Vector {
x: 0.0,
y: -1.0,
z: 0.0
}
);
let west = C3Vector {
x: 0.0,
y: 1.0,
z: 0.0,
};
let blender_west = transform_position(west, CoordinateSystem::Blender);
assert_eq!(
blender_west,
C3Vector {
x: 1.0,
y: 0.0,
z: 0.0
}
);
let up = C3Vector {
x: 0.0,
y: 0.0,
z: 1.0,
};
let blender_up = transform_position(up, CoordinateSystem::Blender);
assert_eq!(
blender_up,
C3Vector {
x: 0.0,
y: 0.0,
z: 1.0
}
); }
}