Skip to content

Commit 9f628dd

Browse files
authored
#543 Fix instanced transformation order (#545)
* save animation transformation in Mesh instead of current transformation * Fix order of transformations for InstancedMesh
1 parent fd0aa8c commit 9f628dd

File tree

3 files changed

+31
-24
lines changed

3 files changed

+31
-24
lines changed

src/renderer/geometry/instanced_mesh.rs

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ pub struct InstancedMesh {
2121
last_camera_position: RwLock<Option<Vec3>>,
2222
aabb: AxisAlignedBoundingBox, // The AABB for the base mesh without transformations applied
2323
transformation: Mat4,
24-
current_transformation: Mat4,
24+
animation_transformation: Mat4,
2525
animation: Option<Box<dyn Fn(f32) -> Mat4 + Send + Sync>>,
2626
instances: Instances,
2727
}
@@ -51,7 +51,7 @@ impl InstancedMesh {
5151
indices: RwLock::new((0..instances.transformations.len()).collect::<Vec<usize>>()),
5252
aabb,
5353
transformation: Mat4::identity(),
54-
current_transformation: Mat4::identity(),
54+
animation_transformation: Mat4::identity(),
5555
animation: None,
5656
instances: instances.clone(),
5757
};
@@ -68,21 +68,21 @@ impl InstancedMesh {
6868

6969
///
7070
/// Set the local to world transformation applied to all instances.
71-
/// This is applied before the transform for each instance.
71+
/// This transformation is applied last, ie. after the instance transformation defined in [Self::set_instances] and the animation transformation defined by [Self::set_animation].
7272
///
7373
pub fn set_transformation(&mut self, transformation: Mat4) {
7474
self.transformation = transformation;
75-
self.current_transformation = transformation;
7675
*self.last_camera_position.write().unwrap() = None;
7776
}
7877

7978
///
8079
/// Specifies a function which takes a time parameter as input and returns a transformation that should be applied to this mesh at the given time.
8180
/// To actually animate this instanced mesh, call [Geometry::animate] at each frame which in turn evaluates the animation function defined by this method.
82-
/// This transformation is applied first, then the local to world transformation defined by [Self::set_transformation].
81+
/// This transformation is applied first, then the instance transformation defined in [Self::set_instances], then the local to world transformation defined by [Self::set_transformation].
8382
///
8483
pub fn set_animation(&mut self, animation: impl Fn(f32) -> Mat4 + Send + Sync + 'static) {
8584
self.animation = Some(Box::new(animation));
85+
self.animate(0.0);
8686
}
8787

8888
/// Returns the number of instances that is rendered.
@@ -185,7 +185,7 @@ impl Geometry for InstancedMesh {
185185
.transformations
186186
.iter()
187187
.map(|m| {
188-
(m * self.current_transformation)
188+
(self.transformation * m * self.animation_transformation)
189189
.w
190190
.truncate()
191191
.distance2(viewer.position())
@@ -200,7 +200,8 @@ impl Geometry for InstancedMesh {
200200
}
201201

202202
program.use_uniform("viewProjection", viewer.projection() * viewer.view());
203-
program.use_uniform("modelMatrix", self.current_transformation);
203+
program.use_uniform("animationTransform", self.animation_transformation);
204+
program.use_uniform("modelMatrix", self.transformation);
204205

205206
let (row1, row2, row3) = &*self.transform.read().unwrap();
206207
program.use_instance_attribute("row1", row1);
@@ -254,16 +255,17 @@ impl Geometry for InstancedMesh {
254255

255256
fn aabb(&self) -> AxisAlignedBoundingBox {
256257
let mut aabb = AxisAlignedBoundingBox::EMPTY;
257-
let local_aabb = self.aabb.transformed(self.current_transformation);
258-
for transformation in &self.instances.transformations {
259-
aabb.expand_with_aabb(local_aabb.transformed(*transformation));
258+
for instance_transformation in &self.instances.transformations {
259+
aabb.expand_with_aabb(self.aabb.transformed(
260+
self.transformation * instance_transformation * self.animation_transformation,
261+
));
260262
}
261263
aabb
262264
}
263265

264266
fn animate(&mut self, time: f32) {
265267
if let Some(animation) = &self.animation {
266-
self.current_transformation = self.transformation * animation(time);
268+
self.animation_transformation = animation(time);
267269
*self.last_camera_position.write().unwrap() = None;
268270
}
269271
}
@@ -307,6 +309,7 @@ impl Geometry for InstancedMesh {
307309
#[derive(Clone, Debug, Default)]
308310
pub struct Instances {
309311
/// The transformations applied to each instance.
312+
/// This transformation is applied in between the animation transformation defined by [InstancedMesh::set_animation] and the transformation defined in [InstancedMesh::set_transformation].
310313
pub transformations: Vec<Mat4>,
311314
/// The texture transform applied to the uv coordinates of each instance.
312315
pub texture_transformations: Option<Vec<Mat3>>,

src/renderer/geometry/mesh.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ pub struct Mesh {
1111
context: Context,
1212
aabb: AxisAlignedBoundingBox,
1313
transformation: Mat4,
14-
current_transformation: Mat4,
14+
animation_transformation: Mat4,
1515
animation: Option<Box<dyn Fn(f32) -> Mat4 + Send + Sync>>,
1616
}
1717

@@ -27,7 +27,7 @@ impl Mesh {
2727
base_mesh: BaseMesh::new(context, cpu_mesh),
2828
aabb,
2929
transformation: Mat4::identity(),
30-
current_transformation: Mat4::identity(),
30+
animation_transformation: Mat4::identity(),
3131
animation: None,
3232
}
3333
}
@@ -66,7 +66,6 @@ impl Mesh {
6666
///
6767
pub fn set_transformation(&mut self, transformation: Mat4) {
6868
self.transformation = transformation;
69-
self.current_transformation = transformation;
7069
}
7170

7271
///
@@ -76,6 +75,7 @@ impl Mesh {
7675
///
7776
pub fn set_animation(&mut self, animation: impl Fn(f32) -> Mat4 + Send + Sync + 'static) {
7877
self.animation = Some(Box::new(animation));
78+
self.animate(0.0);
7979
}
8080

8181
///
@@ -145,25 +145,27 @@ impl<'a> IntoIterator for &'a Mesh {
145145

146146
impl Geometry for Mesh {
147147
fn aabb(&self) -> AxisAlignedBoundingBox {
148-
self.aabb.transformed(self.current_transformation)
148+
self.aabb
149+
.transformed(self.transformation * self.animation_transformation)
149150
}
150151

151152
fn animate(&mut self, time: f32) {
152153
if let Some(animation) = &self.animation {
153-
self.current_transformation = self.transformation * animation(time);
154+
self.animation_transformation = animation(time);
154155
}
155156
}
156157

157158
fn draw(&self, viewer: &dyn Viewer, program: &Program, render_states: RenderStates) {
158-
if let Some(inverse) = self.current_transformation.invert() {
159+
let local2world = self.transformation * self.animation_transformation;
160+
if let Some(inverse) = local2world.invert() {
159161
program.use_uniform_if_required("normalMatrix", inverse.transpose());
160162
} else {
161163
// determinant is float zero
162164
return;
163165
}
164166

165167
program.use_uniform("viewProjection", viewer.projection() * viewer.view());
166-
program.use_uniform("modelMatrix", self.current_transformation);
168+
program.use_uniform("modelMatrix", local2world);
167169

168170
self.base_mesh.draw(program, render_states, viewer);
169171
}

src/renderer/geometry/shaders/mesh.vert

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ uniform float time;
1111
#endif
1212

1313
#ifdef USE_INSTANCE_TRANSFORMS
14+
uniform mat4 animationTransform;
1415
in vec4 row1;
1516
in vec4 row2;
1617
in vec4 row3;
@@ -62,17 +63,18 @@ void main()
6263
transform[1] = vec4(row1.y, row2.y, row3.y, 0.0);
6364
transform[2] = vec4(row1.z, row2.z, row3.z, 0.0);
6465
transform[3] = vec4(row1.w, row2.w, row3.w, 1.0);
65-
local2World = transform * local2World;
66+
local2World = local2World * transform * animationTransform;
6667
#endif
6768

68-
vec4 worldPosition = local2World * vec4(position, 1.);
69-
worldPosition /= worldPosition.w;
7069
#ifdef PARTICLES
71-
worldPosition.xyz += start_position + start_velocity * time + 0.5 * acceleration * time * time;
70+
mat4 animationTransform = mat4(1.0);
71+
animationTransform[3].xyz = start_position + start_velocity * time + 0.5 * acceleration * time * time;
72+
local2World = local2World * animationTransform;
7273
#endif
73-
gl_Position = viewProjection * worldPosition;
7474

75-
pos = worldPosition.xyz;
75+
vec4 worldPosition = local2World * vec4(position, 1.);
76+
gl_Position = viewProjection * worldPosition;
77+
pos = worldPosition.xyz / worldPosition.w;
7678

7779
// *** NORMAL ***
7880
#ifdef USE_NORMALS

0 commit comments

Comments
 (0)