fyrox_impl/scene/
transform.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Contains all structures and methods to create and manage 3D transforms.
22//!
23//! `Transform` allows you to combine spatial properties into a single matrix in
24//! an easy manner. It contains many methods that can be used to modify a single
25//! property of a transform which then will be "baked" into the single matrix.
26//!
27//! # Complexity
28//!
29//! Fyrox uses a complex transform model inherited from the [FBX transform formulae](http://download.autodesk.com/us/fbx/20112/FBX_SDK_HELP/index.html?url=WS1a9193826455f5ff1f92379812724681e696651.htm,topicNumber=d0e7429):
30//!
31//! `Transform = T * Roff * Rp * Rpre * R * Rpost * Rp⁻¹ * Soff * Sp * S * Sp⁻¹`
32//!
33//! where  
34//! `T`     - Translation  
35//! `Roff`  - Rotation offset  
36//! `Rp`    - Rotation pivot  
37//! `Rpre`  - Pre-rotation  
38//! `R`     - Rotation  
39//! `Rpost` - Post-rotation  
40//! `Rp⁻¹`  - Inverse of the rotation pivot  
41//! `Soff`  - Scaling offset  
42//! `Sp`    - Scaling pivot  
43//! `S`     - Scaling  
44//! `Sp⁻¹`  - Inverse of the scaling pivot  
45//!
46//! It is very flexible, however it can be slow to computate. To solve possible
47//! performance issues, Fyrox tries to precache every possible component. This means
48//! that we use lazy evaluation: you can setup all the required properties, and the actual
49//! calculations will be delayed until you try to get the matrix from the transform. This makes
50//! calculations faster, but increases the required amount of memory.
51//!
52//! In most cases you don't need to worry about all those properties, you need just `T`, `R`, `S` -
53//! those will cover 99% of your requirements.
54//!
55//! Fun fact: the transform format was dictated by the use of the monster called FBX file format.
56//! Some libraries (like assimp) decompose this complex formula into a set of smaller transforms
57//! which contain only T R S components and then combine them to get the final result, I find
58//! this approach very bug prone and it is still heavy from a computation perspective. It is much
59//! easier to use it as is.
60//!
61//! # Decomposition
62//!
63//! Once the transform is baked into a matrix, it is *almost* impossible to decompose it back into
64//! its initial components, thats why the engine does not provide any methods to get those
65//! properties back.
66
67use crate::core::{
68    algebra::{Matrix3, Matrix4, UnitQuaternion, Vector3},
69    log::{Log, MessageKind},
70    reflect::prelude::*,
71    variable::InheritableVariable,
72    visitor::{Visit, VisitResult, Visitor},
73};
74use std::cell::Cell;
75
76/// See module docs.
77#[derive(Clone, Debug, Reflect)]
78pub struct Transform {
79    // Indicates that some property has changed and matrix must be
80    // recalculated before use. This is some sort of lazy evaluation.
81    #[reflect(hidden)]
82    dirty: Cell<bool>,
83
84    #[reflect(
85        description = "Local scale of the transform",
86        setter = "set_scale_internal",
87        step = 0.1
88    )]
89    local_scale: InheritableVariable<Vector3<f32>>,
90
91    #[reflect(
92        description = "Local position of the transform",
93        setter = "set_position_internal",
94        step = 0.1
95    )]
96    local_position: InheritableVariable<Vector3<f32>>,
97
98    #[reflect(
99        description = "Local rotation of the transform",
100        setter = "set_rotation_internal",
101        step = 1.0
102    )]
103    local_rotation: InheritableVariable<UnitQuaternion<f32>>,
104
105    #[reflect(
106        description = "Pre rotation of the transform. Applied before local rotation.",
107        setter = "set_pre_rotation_internal",
108        step = 1.0
109    )]
110    pre_rotation: InheritableVariable<UnitQuaternion<f32>>,
111
112    #[reflect(
113        description = "Post rotation of the transform. Applied after local rotation.",
114        setter = "set_post_rotation_internal",
115        step = 1.0
116    )]
117    post_rotation: InheritableVariable<UnitQuaternion<f32>>,
118
119    #[reflect(
120        description = "Rotation offset of the transform.",
121        setter = "set_rotation_offset_internal",
122        step = 0.1
123    )]
124    rotation_offset: InheritableVariable<Vector3<f32>>,
125
126    #[reflect(
127        description = "Rotation pivot of the transform.",
128        setter = "set_rotation_pivot_internal",
129        step = 0.1
130    )]
131    rotation_pivot: InheritableVariable<Vector3<f32>>,
132
133    #[reflect(
134        description = "Scale offset of the transform.",
135        setter = "set_scaling_offset_internal",
136        step = 0.1
137    )]
138    scaling_offset: InheritableVariable<Vector3<f32>>,
139
140    #[reflect(
141        description = "Scale pivot of the transform.",
142        setter = "set_scaling_pivot_internal",
143        step = 0.1
144    )]
145    scaling_pivot: InheritableVariable<Vector3<f32>>,
146
147    // Combined transform. Final result of combination of other properties.
148    #[reflect(hidden)]
149    matrix: Cell<Matrix4<f32>>,
150
151    #[reflect(hidden)]
152    post_rotation_matrix: Matrix3<f32>,
153}
154
155impl Visit for Transform {
156    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
157        let mut region = visitor.enter_region(name)?;
158
159        self.local_scale.visit("LocalScale", &mut region)?;
160        self.local_position.visit("LocalPosition", &mut region)?;
161        self.local_rotation.visit("LocalRotation", &mut region)?;
162        self.pre_rotation.visit("PreRotation", &mut region)?;
163        self.post_rotation.visit("PostRotation", &mut region)?;
164        self.rotation_offset.visit("RotationOffset", &mut region)?;
165        self.rotation_pivot.visit("RotationPivot", &mut region)?;
166        self.scaling_offset.visit("ScalingOffset", &mut region)?;
167        self.scaling_pivot.visit("ScalingPivot", &mut region)?;
168
169        drop(region);
170
171        if visitor.is_reading() {
172            self.post_rotation_matrix =
173                build_post_rotation_matrix(self.post_rotation.clone_inner());
174        }
175
176        Ok(())
177    }
178}
179
180impl Default for Transform {
181    fn default() -> Self {
182        Self::identity()
183    }
184}
185
186fn build_post_rotation_matrix(post_rotation: UnitQuaternion<f32>) -> Matrix3<f32> {
187    post_rotation
188        .to_rotation_matrix()
189        .matrix()
190        .try_inverse()
191        .unwrap_or_else(|| {
192            Log::writeln(
193                MessageKind::Warning,
194                "Unable to inverse post rotation matrix! Fallback to identity matrix.",
195            );
196            Matrix3::identity()
197        })
198}
199
200impl Transform {
201    /// Creates new transform that has no effect, in other words any vector
202    /// or matrix will remain unchanged if combined with identity transform.
203    pub fn identity() -> Self {
204        Self {
205            dirty: Cell::new(true),
206            local_position: InheritableVariable::new_modified(Vector3::default()),
207            local_scale: InheritableVariable::new_modified(Vector3::new(1.0, 1.0, 1.0)),
208            local_rotation: InheritableVariable::new_modified(UnitQuaternion::identity()),
209            pre_rotation: InheritableVariable::new_modified(UnitQuaternion::identity()),
210            post_rotation: InheritableVariable::new_modified(UnitQuaternion::identity()),
211            rotation_offset: InheritableVariable::new_modified(Vector3::default()),
212            rotation_pivot: InheritableVariable::new_modified(Vector3::default()),
213            scaling_offset: InheritableVariable::new_modified(Vector3::default()),
214            scaling_pivot: InheritableVariable::new_modified(Vector3::default()),
215            matrix: Cell::new(Matrix4::identity()),
216            post_rotation_matrix: Matrix3::identity(),
217        }
218    }
219
220    /// Returns current position of transform.
221    #[inline]
222    pub fn position(&self) -> &InheritableVariable<Vector3<f32>> {
223        &self.local_position
224    }
225
226    /// Sets position of transform.
227    #[inline]
228    pub fn set_position(&mut self, local_position: Vector3<f32>) -> &mut Self {
229        if self.dirty.get() || *self.local_position != local_position {
230            self.set_position_internal(local_position);
231        }
232        self
233    }
234
235    #[inline]
236    fn set_position_internal(&mut self, local_position: Vector3<f32>) -> Vector3<f32> {
237        self.dirty.set(true);
238        self.local_position
239            .set_value_and_mark_modified(local_position)
240    }
241
242    /// Returns current rotation quaternion of transform.
243    #[inline]
244    pub fn rotation(&self) -> &InheritableVariable<UnitQuaternion<f32>> {
245        &self.local_rotation
246    }
247
248    /// Sets rotation of transform.
249    #[inline]
250    pub fn set_rotation(&mut self, local_rotation: UnitQuaternion<f32>) -> &mut Self {
251        if self.dirty.get() || *self.local_rotation != local_rotation {
252            self.set_rotation_internal(local_rotation);
253        }
254        self
255    }
256
257    #[inline]
258    fn set_rotation_internal(
259        &mut self,
260        local_rotation: UnitQuaternion<f32>,
261    ) -> UnitQuaternion<f32> {
262        self.dirty.set(true);
263        self.local_rotation
264            .set_value_and_mark_modified(local_rotation)
265    }
266
267    /// Returns current scale factor of transform.
268    #[inline]
269    pub fn scale(&self) -> &InheritableVariable<Vector3<f32>> {
270        &self.local_scale
271    }
272
273    /// Sets scale of transform.
274    #[inline]
275    pub fn set_scale(&mut self, local_scale: Vector3<f32>) -> &mut Self {
276        if self.dirty.get() || *self.local_scale != local_scale {
277            self.set_scale_internal(local_scale);
278        }
279        self
280    }
281
282    #[inline]
283    fn set_scale_internal(&mut self, local_scale: Vector3<f32>) -> Vector3<f32> {
284        self.dirty.set(true);
285        self.local_scale.set_value_and_mark_modified(local_scale)
286    }
287
288    /// Sets pre-rotation of transform. Usually pre-rotation can be used to change
289    /// "coordinate" system of transform. It is mostly for FBX compatibility, and
290    /// never used in other places of engine.
291    #[inline]
292    pub fn set_pre_rotation(&mut self, pre_rotation: UnitQuaternion<f32>) -> &mut Self {
293        if self.dirty.get() || *self.pre_rotation != pre_rotation {
294            self.set_pre_rotation_internal(pre_rotation);
295        }
296        self
297    }
298
299    #[inline]
300    fn set_pre_rotation_internal(
301        &mut self,
302        pre_rotation: UnitQuaternion<f32>,
303    ) -> UnitQuaternion<f32> {
304        self.dirty.set(true);
305        self.pre_rotation.set_value_and_mark_modified(pre_rotation)
306    }
307
308    /// Returns current pre-rotation of transform.
309    #[inline]
310    pub fn pre_rotation(&self) -> &InheritableVariable<UnitQuaternion<f32>> {
311        &self.pre_rotation
312    }
313
314    /// Sets post-rotation of transform. Usually post-rotation can be used to change
315    /// "coordinate" system of transform. It is mostly for FBX compatibility, and
316    /// never used in other places of engine.
317    #[inline]
318    pub fn set_post_rotation(&mut self, post_rotation: UnitQuaternion<f32>) -> &mut Self {
319        if self.dirty.get() || *self.post_rotation != post_rotation {
320            self.set_post_rotation_internal(post_rotation);
321        }
322        self
323    }
324
325    #[inline]
326    fn set_post_rotation_internal(
327        &mut self,
328        post_rotation: UnitQuaternion<f32>,
329    ) -> UnitQuaternion<f32> {
330        self.post_rotation_matrix = build_post_rotation_matrix(post_rotation);
331        self.dirty.set(true);
332        self.post_rotation
333            .set_value_and_mark_modified(post_rotation)
334    }
335
336    /// Returns current post-rotation of transform.
337    #[inline]
338    pub fn post_rotation(&self) -> &InheritableVariable<UnitQuaternion<f32>> {
339        &self.post_rotation
340    }
341
342    /// Sets rotation offset of transform. Moves rotation pivot using given vector,
343    /// it results in rotation being performed around rotation pivot with some offset.
344    #[inline]
345    pub fn set_rotation_offset(&mut self, rotation_offset: Vector3<f32>) -> &mut Self {
346        if self.dirty.get() || *self.rotation_offset != rotation_offset {
347            self.set_rotation_offset_internal(rotation_offset);
348        }
349        self
350    }
351
352    #[inline]
353    fn set_rotation_offset_internal(&mut self, rotation_offset: Vector3<f32>) -> Vector3<f32> {
354        self.dirty.set(true);
355        self.rotation_offset
356            .set_value_and_mark_modified(rotation_offset)
357    }
358
359    /// Returns current rotation offset of transform.
360    #[inline]
361    pub fn rotation_offset(&self) -> &InheritableVariable<Vector3<f32>> {
362        &self.rotation_offset
363    }
364
365    /// Sets rotation pivot of transform. This method sets a point around which all
366    /// rotations will be performed. For example it can be used to rotate a cube around
367    /// its vertex.
368    #[inline]
369    pub fn set_rotation_pivot(&mut self, rotation_pivot: Vector3<f32>) -> &mut Self {
370        if self.dirty.get() || *self.rotation_pivot != rotation_pivot {
371            self.set_rotation_pivot_internal(rotation_pivot);
372        }
373        self
374    }
375
376    #[inline]
377    fn set_rotation_pivot_internal(&mut self, rotation_pivot: Vector3<f32>) -> Vector3<f32> {
378        self.dirty.set(true);
379        self.rotation_pivot
380            .set_value_and_mark_modified(rotation_pivot)
381    }
382
383    /// Returns current rotation pivot of transform.
384    #[inline]
385    pub fn rotation_pivot(&self) -> &InheritableVariable<Vector3<f32>> {
386        &self.rotation_pivot
387    }
388
389    /// Sets scaling offset. Scaling offset defines offset from position of scaling
390    /// pivot.
391    #[inline]
392    pub fn set_scaling_offset(&mut self, scaling_offset: Vector3<f32>) -> &mut Self {
393        if self.dirty.get() || *self.scaling_offset != scaling_offset {
394            self.set_scaling_offset_internal(scaling_offset);
395            self.dirty.set(true);
396        }
397        self
398    }
399
400    #[inline]
401    fn set_scaling_offset_internal(&mut self, scaling_offset: Vector3<f32>) -> Vector3<f32> {
402        self.dirty.set(true);
403        self.scaling_offset
404            .set_value_and_mark_modified(scaling_offset)
405    }
406
407    /// Returns current scaling offset of transform.
408    #[inline]
409    pub fn scaling_offset(&self) -> &InheritableVariable<Vector3<f32>> {
410        &self.scaling_offset
411    }
412
413    /// Sets scaling pivot. Scaling pivot sets a point around which scale will be
414    /// performed.
415    #[inline]
416    pub fn set_scaling_pivot(&mut self, scaling_pivot: Vector3<f32>) -> &mut Self {
417        if self.dirty.get() || *self.scaling_pivot != scaling_pivot {
418            self.set_scaling_pivot_internal(scaling_pivot);
419            self.dirty.set(true);
420        }
421        self
422    }
423
424    #[inline]
425    fn set_scaling_pivot_internal(&mut self, scaling_pivot: Vector3<f32>) -> Vector3<f32> {
426        self.dirty.set(true);
427        self.scaling_pivot
428            .set_value_and_mark_modified(scaling_pivot)
429    }
430
431    /// Returns current scaling pivot of transform.
432    #[inline]
433    pub fn scaling_pivot(&self) -> &InheritableVariable<Vector3<f32>> {
434        &self.scaling_pivot
435    }
436
437    /// Shifts local position using given vector. It is a shortcut for:
438    /// set_position(position() + offset)
439    #[inline]
440    pub fn offset(&mut self, vec: Vector3<f32>) -> &mut Self {
441        self.local_position
442            .set_value_and_mark_modified(*self.local_position + vec);
443        self.dirty.set(true);
444        self
445    }
446
447    fn calculate_local_transform(&self) -> Matrix4<f32> {
448        // Make shortcuts to remove visual clutter.
449        let por = &self.post_rotation_matrix;
450        let pr = *self.pre_rotation.to_rotation_matrix().matrix();
451        let r = *self.local_rotation.to_rotation_matrix().matrix();
452
453        let sx = self.local_scale.x;
454        let sy = self.local_scale.y;
455        let sz = self.local_scale.z;
456
457        let tx = self.local_position.x;
458        let ty = self.local_position.y;
459        let tz = self.local_position.z;
460
461        let rpx = self.rotation_pivot.x;
462        let rpy = self.rotation_pivot.y;
463        let rpz = self.rotation_pivot.z;
464
465        let rox = self.rotation_offset.x;
466        let roy = self.rotation_offset.y;
467        let roz = self.rotation_offset.z;
468
469        let spx = self.scaling_pivot.x;
470        let spy = self.scaling_pivot.y;
471        let spz = self.scaling_pivot.z;
472
473        let sox = self.scaling_offset.x;
474        let soy = self.scaling_offset.y;
475        let soz = self.scaling_offset.z;
476
477        // Optimized multiplication of these matrices:
478        //
479        // Transform = T * Roff * Rp * Rpre * R * Rpost * Rp⁻¹ * Soff * Sp * S * Sp⁻¹
480        //
481        // where
482        // T     - Translation
483        // Roff  - Rotation offset
484        // Rp    - Rotation pivot
485        // Rpre  - Pre-rotation
486        // R     - Rotation
487        // Rpost - Post-rotation
488        // Rp⁻¹  - Inverse of the rotation pivot
489        // Soff  - Scaling offset
490        // Sp    - Scaling pivot
491        // S     - Scaling
492        // Sp⁻¹  - Inverse of the scaling pivot
493        let a0 = pr[0] * r[0] + pr[3] * r[1] + pr[6] * r[2];
494        let a1 = pr[1] * r[0] + pr[4] * r[1] + pr[7] * r[2];
495        let a2 = pr[2] * r[0] + pr[5] * r[1] + pr[8] * r[2];
496        let a3 = pr[0] * r[3] + pr[3] * r[4] + pr[6] * r[5];
497        let a4 = pr[1] * r[3] + pr[4] * r[4] + pr[7] * r[5];
498        let a5 = pr[2] * r[3] + pr[5] * r[4] + pr[8] * r[5];
499        let a6 = pr[0] * r[6] + pr[3] * r[7] + pr[6] * r[8];
500        let a7 = pr[1] * r[6] + pr[4] * r[7] + pr[7] * r[8];
501        let a8 = pr[2] * r[6] + pr[5] * r[7] + pr[8] * r[8];
502        let f0 = por[0] * a0 + por[1] * a3 + por[2] * a6;
503        let f1 = por[0] * a1 + por[1] * a4 + por[2] * a7;
504        let f2 = por[0] * a2 + por[1] * a5 + por[2] * a8;
505        let f3 = por[3] * a0 + por[4] * a3 + por[5] * a6;
506        let f4 = por[3] * a1 + por[4] * a4 + por[5] * a7;
507        let f5 = por[3] * a2 + por[4] * a5 + por[5] * a8;
508        let f6 = por[6] * a0 + por[7] * a3 + por[8] * a6;
509        let f7 = por[6] * a1 + por[7] * a4 + por[8] * a7;
510        let f8 = por[6] * a2 + por[7] * a5 + por[8] * a8;
511        let m0 = sx * f0;
512        let m1 = sx * f1;
513        let m2 = sx * f2;
514        let m3 = 0.0;
515        let m4 = sy * f3;
516        let m5 = sy * f4;
517        let m6 = sy * f5;
518        let m7 = 0.0;
519        let m8 = sz * f6;
520        let m9 = sz * f7;
521        let m10 = sz * f8;
522        let m11 = 0.0;
523        let k0 = spx * f0;
524        let k1 = spy * f3;
525        let k2 = spz * f6;
526        let m12 = rox + rpx + tx - rpx * f0 - rpy * f3 - rpz * f6
527            + sox * f0
528            + k0
529            + soy * f3
530            + k1
531            + soz * f6
532            + k2
533            - sx * k0
534            - sy * k1
535            - sz * k2;
536        let k3 = spx * f1;
537        let k4 = spy * f4;
538        let k5 = spz * f7;
539        let m13 = roy + rpy + ty - rpx * f1 - rpy * f4 - rpz * f7
540            + sox * f1
541            + k3
542            + soy * f4
543            + k4
544            + soz * f7
545            + k5
546            - sx * k3
547            - sy * k4
548            - sz * k5;
549        let k6 = spx * f2;
550        let k7 = spy * f5;
551        let k8 = spz * f8;
552        let m14 = roz + rpz + tz - rpx * f2 - rpy * f5 - rpz * f8
553            + sox * f2
554            + k6
555            + soy * f5
556            + k7
557            + soz * f8
558            + k8
559            - sx * k6
560            - sy * k7
561            - sz * k8;
562        let m15 = 1.0;
563        Matrix4::new(
564            m0, m4, m8, m12, m1, m5, m9, m13, m2, m6, m10, m14, m3, m7, m11, m15,
565        )
566    }
567
568    /// Returns matrix which is final result of transform. Matrix then can be used to transform
569    /// a vector, or combine with other matrix, to make transform hierarchy for example.
570    pub fn matrix(&self) -> Matrix4<f32> {
571        if self.dirty.get() {
572            self.matrix.set(self.calculate_local_transform());
573            self.dirty.set(false)
574        }
575        self.matrix.get()
576    }
577}
578
579/// Transform builder allows you to construct transform in declarative manner.
580/// This is typical implementation of Builder pattern.
581pub struct TransformBuilder {
582    local_scale: Vector3<f32>,
583    local_position: Vector3<f32>,
584    local_rotation: UnitQuaternion<f32>,
585    pre_rotation: UnitQuaternion<f32>,
586    post_rotation: UnitQuaternion<f32>,
587    rotation_offset: Vector3<f32>,
588    rotation_pivot: Vector3<f32>,
589    scaling_offset: Vector3<f32>,
590    scaling_pivot: Vector3<f32>,
591}
592
593impl Default for TransformBuilder {
594    fn default() -> Self {
595        Self::new()
596    }
597}
598
599impl TransformBuilder {
600    /// Creates new transform builder. If it won't be modified then it will produce
601    /// identity transform as result.
602    pub fn new() -> Self {
603        Self {
604            local_scale: Vector3::new(1.0, 1.0, 1.0),
605            local_position: Default::default(),
606            local_rotation: UnitQuaternion::identity(),
607            pre_rotation: UnitQuaternion::identity(),
608            post_rotation: UnitQuaternion::identity(),
609            rotation_offset: Default::default(),
610            rotation_pivot: Default::default(),
611            scaling_offset: Default::default(),
612            scaling_pivot: Default::default(),
613        }
614    }
615
616    /// Sets desired local scale.
617    pub fn with_local_scale(mut self, scale: Vector3<f32>) -> Self {
618        self.local_scale = scale;
619        self
620    }
621
622    /// Sets desired local position.
623    pub fn with_local_position(mut self, position: Vector3<f32>) -> Self {
624        self.local_position = position;
625        self
626    }
627
628    /// Sets desired local rotation.
629    pub fn with_local_rotation(mut self, rotation: UnitQuaternion<f32>) -> Self {
630        self.local_rotation = rotation;
631        self
632    }
633
634    /// Sets desired pre-rotation.
635    pub fn with_pre_rotation(mut self, rotation: UnitQuaternion<f32>) -> Self {
636        self.pre_rotation = rotation;
637        self
638    }
639
640    /// Sets desired post-rotation.
641    pub fn with_post_rotation(mut self, rotation: UnitQuaternion<f32>) -> Self {
642        self.post_rotation = rotation;
643        self
644    }
645
646    /// Sets desired rotation offset.
647    pub fn with_rotation_offset(mut self, offset: Vector3<f32>) -> Self {
648        self.rotation_offset = offset;
649        self
650    }
651
652    /// Sets desired rotation pivot.
653    pub fn with_rotation_pivot(mut self, pivot: Vector3<f32>) -> Self {
654        self.rotation_pivot = pivot;
655        self
656    }
657
658    /// Sets desired scaling offset.
659    pub fn with_scaling_offset(mut self, offset: Vector3<f32>) -> Self {
660        self.scaling_offset = offset;
661        self
662    }
663
664    /// Sets desired scaling pivot.
665    pub fn with_scaling_pivot(mut self, pivot: Vector3<f32>) -> Self {
666        self.scaling_pivot = pivot;
667        self
668    }
669
670    /// Builds new Transform instance using provided values.
671    pub fn build(self) -> Transform {
672        Transform {
673            dirty: Cell::new(true),
674            local_scale: self.local_scale.into(),
675            local_position: self.local_position.into(),
676            local_rotation: self.local_rotation.into(),
677            pre_rotation: self.pre_rotation.into(),
678            post_rotation: self.post_rotation.into(),
679            rotation_offset: self.rotation_offset.into(),
680            rotation_pivot: self.rotation_pivot.into(),
681            scaling_offset: self.scaling_offset.into(),
682            scaling_pivot: self.scaling_pivot.into(),
683            matrix: Cell::new(Matrix4::identity()),
684            post_rotation_matrix: build_post_rotation_matrix(self.post_rotation),
685        }
686    }
687}