Skip to main content

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    /// Local scale of the transform
85    #[reflect(setter = "set_scale_internal", step = 0.1)]
86    local_scale: InheritableVariable<Vector3<f32>>,
87
88    /// Local position of the transform
89    #[reflect(setter = "set_position_internal", step = 0.1)]
90    local_position: InheritableVariable<Vector3<f32>>,
91
92    /// Local rotation of the transform
93    #[reflect(setter = "set_rotation_internal", step = 1.0)]
94    local_rotation: InheritableVariable<UnitQuaternion<f32>>,
95
96    /// Pre rotation of the transform. Applied before local rotation.
97    #[reflect(setter = "set_pre_rotation_internal", step = 1.0)]
98    pre_rotation: InheritableVariable<UnitQuaternion<f32>>,
99
100    /// Post rotation of the transform. Applied after local rotation.
101    #[reflect(setter = "set_post_rotation_internal", step = 1.0)]
102    post_rotation: InheritableVariable<UnitQuaternion<f32>>,
103
104    /// Rotation offset of the transform.
105    #[reflect(setter = "set_rotation_offset_internal", step = 0.1)]
106    rotation_offset: InheritableVariable<Vector3<f32>>,
107
108    /// Rotation pivot of the transform.
109    #[reflect(setter = "set_rotation_pivot_internal", step = 0.1)]
110    rotation_pivot: InheritableVariable<Vector3<f32>>,
111
112    /// Scale offset of the transform.
113    #[reflect(setter = "set_scaling_offset_internal", step = 0.1)]
114    scaling_offset: InheritableVariable<Vector3<f32>>,
115
116    /// Scale pivot of the transform.
117    #[reflect(setter = "set_scaling_pivot_internal", step = 0.1)]
118    scaling_pivot: InheritableVariable<Vector3<f32>>,
119
120    // Combined transform. Final result of combination of other properties.
121    #[reflect(hidden)]
122    matrix: Cell<Matrix4<f32>>,
123
124    #[reflect(hidden)]
125    post_rotation_matrix: Matrix3<f32>,
126}
127
128impl Visit for Transform {
129    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
130        let mut region = visitor.enter_region(name)?;
131
132        self.local_scale.visit("LocalScale", &mut region)?;
133        self.local_position.visit("LocalPosition", &mut region)?;
134        self.local_rotation.visit("LocalRotation", &mut region)?;
135        self.pre_rotation.visit("PreRotation", &mut region)?;
136        self.post_rotation.visit("PostRotation", &mut region)?;
137        self.rotation_offset.visit("RotationOffset", &mut region)?;
138        self.rotation_pivot.visit("RotationPivot", &mut region)?;
139        self.scaling_offset.visit("ScalingOffset", &mut region)?;
140        self.scaling_pivot.visit("ScalingPivot", &mut region)?;
141
142        drop(region);
143
144        if visitor.is_reading() {
145            self.post_rotation_matrix =
146                build_post_rotation_matrix(self.post_rotation.clone_inner());
147        }
148
149        Ok(())
150    }
151}
152
153impl Default for Transform {
154    fn default() -> Self {
155        Self::identity()
156    }
157}
158
159fn build_post_rotation_matrix(post_rotation: UnitQuaternion<f32>) -> Matrix3<f32> {
160    post_rotation
161        .to_rotation_matrix()
162        .matrix()
163        .try_inverse()
164        .unwrap_or_else(|| {
165            Log::writeln(
166                MessageKind::Warning,
167                "Unable to inverse post rotation matrix! Fallback to identity matrix.",
168            );
169            Matrix3::identity()
170        })
171}
172
173impl Transform {
174    /// Creates new transform that has no effect, in other words any vector
175    /// or matrix will remain unchanged if combined with identity transform.
176    pub fn identity() -> Self {
177        Self {
178            dirty: Cell::new(true),
179            local_position: InheritableVariable::new_modified(Vector3::default()),
180            local_scale: InheritableVariable::new_modified(Vector3::new(1.0, 1.0, 1.0)),
181            local_rotation: InheritableVariable::new_modified(UnitQuaternion::identity()),
182            pre_rotation: InheritableVariable::new_modified(UnitQuaternion::identity()),
183            post_rotation: InheritableVariable::new_modified(UnitQuaternion::identity()),
184            rotation_offset: InheritableVariable::new_modified(Vector3::default()),
185            rotation_pivot: InheritableVariable::new_modified(Vector3::default()),
186            scaling_offset: InheritableVariable::new_modified(Vector3::default()),
187            scaling_pivot: InheritableVariable::new_modified(Vector3::default()),
188            matrix: Cell::new(Matrix4::identity()),
189            post_rotation_matrix: Matrix3::identity(),
190        }
191    }
192
193    /// Returns current position of transform.
194    #[inline]
195    pub fn position(&self) -> &InheritableVariable<Vector3<f32>> {
196        &self.local_position
197    }
198
199    /// Sets position of transform.
200    #[inline]
201    pub fn set_position(&mut self, local_position: Vector3<f32>) -> &mut Self {
202        if self.dirty.get() || *self.local_position != local_position {
203            self.set_position_internal(local_position);
204        }
205        self
206    }
207
208    #[inline]
209    fn set_position_internal(&mut self, local_position: Vector3<f32>) -> Vector3<f32> {
210        self.dirty.set(true);
211        self.local_position
212            .set_value_and_mark_modified(local_position)
213    }
214
215    /// Returns current rotation quaternion of transform.
216    #[inline]
217    pub fn rotation(&self) -> &InheritableVariable<UnitQuaternion<f32>> {
218        &self.local_rotation
219    }
220
221    /// Sets rotation of transform.
222    #[inline]
223    pub fn set_rotation(&mut self, local_rotation: UnitQuaternion<f32>) -> &mut Self {
224        if self.dirty.get() || *self.local_rotation != local_rotation {
225            self.set_rotation_internal(local_rotation);
226        }
227        self
228    }
229
230    #[inline]
231    fn set_rotation_internal(
232        &mut self,
233        local_rotation: UnitQuaternion<f32>,
234    ) -> UnitQuaternion<f32> {
235        self.dirty.set(true);
236        self.local_rotation
237            .set_value_and_mark_modified(local_rotation)
238    }
239
240    /// Returns current scale factor of transform.
241    #[inline]
242    pub fn scale(&self) -> &InheritableVariable<Vector3<f32>> {
243        &self.local_scale
244    }
245
246    /// Sets scale of transform.
247    #[inline]
248    pub fn set_scale(&mut self, local_scale: Vector3<f32>) -> &mut Self {
249        if self.dirty.get() || *self.local_scale != local_scale {
250            self.set_scale_internal(local_scale);
251        }
252        self
253    }
254
255    #[inline]
256    fn set_scale_internal(&mut self, local_scale: Vector3<f32>) -> Vector3<f32> {
257        self.dirty.set(true);
258        self.local_scale.set_value_and_mark_modified(local_scale)
259    }
260
261    /// Sets pre-rotation of transform. Usually pre-rotation can be used to change
262    /// "coordinate" system of transform. It is mostly for FBX compatibility, and
263    /// never used in other places of engine.
264    #[inline]
265    pub fn set_pre_rotation(&mut self, pre_rotation: UnitQuaternion<f32>) -> &mut Self {
266        if self.dirty.get() || *self.pre_rotation != pre_rotation {
267            self.set_pre_rotation_internal(pre_rotation);
268        }
269        self
270    }
271
272    #[inline]
273    fn set_pre_rotation_internal(
274        &mut self,
275        pre_rotation: UnitQuaternion<f32>,
276    ) -> UnitQuaternion<f32> {
277        self.dirty.set(true);
278        self.pre_rotation.set_value_and_mark_modified(pre_rotation)
279    }
280
281    /// Returns current pre-rotation of transform.
282    #[inline]
283    pub fn pre_rotation(&self) -> &InheritableVariable<UnitQuaternion<f32>> {
284        &self.pre_rotation
285    }
286
287    /// Sets post-rotation of transform. Usually post-rotation can be used to change
288    /// "coordinate" system of transform. It is mostly for FBX compatibility, and
289    /// never used in other places of engine.
290    #[inline]
291    pub fn set_post_rotation(&mut self, post_rotation: UnitQuaternion<f32>) -> &mut Self {
292        if self.dirty.get() || *self.post_rotation != post_rotation {
293            self.set_post_rotation_internal(post_rotation);
294        }
295        self
296    }
297
298    #[inline]
299    fn set_post_rotation_internal(
300        &mut self,
301        post_rotation: UnitQuaternion<f32>,
302    ) -> UnitQuaternion<f32> {
303        self.post_rotation_matrix = build_post_rotation_matrix(post_rotation);
304        self.dirty.set(true);
305        self.post_rotation
306            .set_value_and_mark_modified(post_rotation)
307    }
308
309    /// Returns current post-rotation of transform.
310    #[inline]
311    pub fn post_rotation(&self) -> &InheritableVariable<UnitQuaternion<f32>> {
312        &self.post_rotation
313    }
314
315    /// Sets rotation offset of transform. Moves rotation pivot using given vector,
316    /// it results in rotation being performed around rotation pivot with some offset.
317    #[inline]
318    pub fn set_rotation_offset(&mut self, rotation_offset: Vector3<f32>) -> &mut Self {
319        if self.dirty.get() || *self.rotation_offset != rotation_offset {
320            self.set_rotation_offset_internal(rotation_offset);
321        }
322        self
323    }
324
325    #[inline]
326    fn set_rotation_offset_internal(&mut self, rotation_offset: Vector3<f32>) -> Vector3<f32> {
327        self.dirty.set(true);
328        self.rotation_offset
329            .set_value_and_mark_modified(rotation_offset)
330    }
331
332    /// Returns current rotation offset of transform.
333    #[inline]
334    pub fn rotation_offset(&self) -> &InheritableVariable<Vector3<f32>> {
335        &self.rotation_offset
336    }
337
338    /// Sets rotation pivot of transform. This method sets a point around which all
339    /// rotations will be performed. For example it can be used to rotate a cube around
340    /// its vertex.
341    #[inline]
342    pub fn set_rotation_pivot(&mut self, rotation_pivot: Vector3<f32>) -> &mut Self {
343        if self.dirty.get() || *self.rotation_pivot != rotation_pivot {
344            self.set_rotation_pivot_internal(rotation_pivot);
345        }
346        self
347    }
348
349    #[inline]
350    fn set_rotation_pivot_internal(&mut self, rotation_pivot: Vector3<f32>) -> Vector3<f32> {
351        self.dirty.set(true);
352        self.rotation_pivot
353            .set_value_and_mark_modified(rotation_pivot)
354    }
355
356    /// Returns current rotation pivot of transform.
357    #[inline]
358    pub fn rotation_pivot(&self) -> &InheritableVariable<Vector3<f32>> {
359        &self.rotation_pivot
360    }
361
362    /// Sets scaling offset. Scaling offset defines offset from position of scaling
363    /// pivot.
364    #[inline]
365    pub fn set_scaling_offset(&mut self, scaling_offset: Vector3<f32>) -> &mut Self {
366        if self.dirty.get() || *self.scaling_offset != scaling_offset {
367            self.set_scaling_offset_internal(scaling_offset);
368            self.dirty.set(true);
369        }
370        self
371    }
372
373    #[inline]
374    fn set_scaling_offset_internal(&mut self, scaling_offset: Vector3<f32>) -> Vector3<f32> {
375        self.dirty.set(true);
376        self.scaling_offset
377            .set_value_and_mark_modified(scaling_offset)
378    }
379
380    /// Returns current scaling offset of transform.
381    #[inline]
382    pub fn scaling_offset(&self) -> &InheritableVariable<Vector3<f32>> {
383        &self.scaling_offset
384    }
385
386    /// Sets scaling pivot. Scaling pivot sets a point around which scale will be
387    /// performed.
388    #[inline]
389    pub fn set_scaling_pivot(&mut self, scaling_pivot: Vector3<f32>) -> &mut Self {
390        if self.dirty.get() || *self.scaling_pivot != scaling_pivot {
391            self.set_scaling_pivot_internal(scaling_pivot);
392            self.dirty.set(true);
393        }
394        self
395    }
396
397    #[inline]
398    fn set_scaling_pivot_internal(&mut self, scaling_pivot: Vector3<f32>) -> Vector3<f32> {
399        self.dirty.set(true);
400        self.scaling_pivot
401            .set_value_and_mark_modified(scaling_pivot)
402    }
403
404    /// Returns current scaling pivot of transform.
405    #[inline]
406    pub fn scaling_pivot(&self) -> &InheritableVariable<Vector3<f32>> {
407        &self.scaling_pivot
408    }
409
410    /// Shifts local position using given vector. It is a shortcut for:
411    /// set_position(position() + offset)
412    #[inline]
413    pub fn offset(&mut self, vec: Vector3<f32>) -> &mut Self {
414        self.local_position
415            .set_value_and_mark_modified(*self.local_position + vec);
416        self.dirty.set(true);
417        self
418    }
419
420    fn calculate_local_transform(&self) -> Matrix4<f32> {
421        // Make shortcuts to remove visual clutter.
422        let por = &self.post_rotation_matrix;
423        let pr = *self.pre_rotation.to_rotation_matrix().matrix();
424        let r = *self.local_rotation.to_rotation_matrix().matrix();
425
426        let sx = self.local_scale.x;
427        let sy = self.local_scale.y;
428        let sz = self.local_scale.z;
429
430        let tx = self.local_position.x;
431        let ty = self.local_position.y;
432        let tz = self.local_position.z;
433
434        let rpx = self.rotation_pivot.x;
435        let rpy = self.rotation_pivot.y;
436        let rpz = self.rotation_pivot.z;
437
438        let rox = self.rotation_offset.x;
439        let roy = self.rotation_offset.y;
440        let roz = self.rotation_offset.z;
441
442        let spx = self.scaling_pivot.x;
443        let spy = self.scaling_pivot.y;
444        let spz = self.scaling_pivot.z;
445
446        let sox = self.scaling_offset.x;
447        let soy = self.scaling_offset.y;
448        let soz = self.scaling_offset.z;
449
450        // Optimized multiplication of these matrices:
451        //
452        // Transform = T * Roff * Rp * Rpre * R * Rpost * Rp⁻¹ * Soff * Sp * S * Sp⁻¹
453        //
454        // where
455        // T     - Translation
456        // Roff  - Rotation offset
457        // Rp    - Rotation pivot
458        // Rpre  - Pre-rotation
459        // R     - Rotation
460        // Rpost - Post-rotation
461        // Rp⁻¹  - Inverse of the rotation pivot
462        // Soff  - Scaling offset
463        // Sp    - Scaling pivot
464        // S     - Scaling
465        // Sp⁻¹  - Inverse of the scaling pivot
466        let a0 = pr[0] * r[0] + pr[3] * r[1] + pr[6] * r[2];
467        let a1 = pr[1] * r[0] + pr[4] * r[1] + pr[7] * r[2];
468        let a2 = pr[2] * r[0] + pr[5] * r[1] + pr[8] * r[2];
469        let a3 = pr[0] * r[3] + pr[3] * r[4] + pr[6] * r[5];
470        let a4 = pr[1] * r[3] + pr[4] * r[4] + pr[7] * r[5];
471        let a5 = pr[2] * r[3] + pr[5] * r[4] + pr[8] * r[5];
472        let a6 = pr[0] * r[6] + pr[3] * r[7] + pr[6] * r[8];
473        let a7 = pr[1] * r[6] + pr[4] * r[7] + pr[7] * r[8];
474        let a8 = pr[2] * r[6] + pr[5] * r[7] + pr[8] * r[8];
475        let f0 = por[0] * a0 + por[1] * a3 + por[2] * a6;
476        let f1 = por[0] * a1 + por[1] * a4 + por[2] * a7;
477        let f2 = por[0] * a2 + por[1] * a5 + por[2] * a8;
478        let f3 = por[3] * a0 + por[4] * a3 + por[5] * a6;
479        let f4 = por[3] * a1 + por[4] * a4 + por[5] * a7;
480        let f5 = por[3] * a2 + por[4] * a5 + por[5] * a8;
481        let f6 = por[6] * a0 + por[7] * a3 + por[8] * a6;
482        let f7 = por[6] * a1 + por[7] * a4 + por[8] * a7;
483        let f8 = por[6] * a2 + por[7] * a5 + por[8] * a8;
484        let m0 = sx * f0;
485        let m1 = sx * f1;
486        let m2 = sx * f2;
487        let m3 = 0.0;
488        let m4 = sy * f3;
489        let m5 = sy * f4;
490        let m6 = sy * f5;
491        let m7 = 0.0;
492        let m8 = sz * f6;
493        let m9 = sz * f7;
494        let m10 = sz * f8;
495        let m11 = 0.0;
496        let k0 = spx * f0;
497        let k1 = spy * f3;
498        let k2 = spz * f6;
499        let m12 = rox + rpx + tx - rpx * f0 - rpy * f3 - rpz * f6
500            + sox * f0
501            + k0
502            + soy * f3
503            + k1
504            + soz * f6
505            + k2
506            - sx * k0
507            - sy * k1
508            - sz * k2;
509        let k3 = spx * f1;
510        let k4 = spy * f4;
511        let k5 = spz * f7;
512        let m13 = roy + rpy + ty - rpx * f1 - rpy * f4 - rpz * f7
513            + sox * f1
514            + k3
515            + soy * f4
516            + k4
517            + soz * f7
518            + k5
519            - sx * k3
520            - sy * k4
521            - sz * k5;
522        let k6 = spx * f2;
523        let k7 = spy * f5;
524        let k8 = spz * f8;
525        let m14 = roz + rpz + tz - rpx * f2 - rpy * f5 - rpz * f8
526            + sox * f2
527            + k6
528            + soy * f5
529            + k7
530            + soz * f8
531            + k8
532            - sx * k6
533            - sy * k7
534            - sz * k8;
535        let m15 = 1.0;
536        Matrix4::new(
537            m0, m4, m8, m12, m1, m5, m9, m13, m2, m6, m10, m14, m3, m7, m11, m15,
538        )
539    }
540
541    /// Returns matrix which is final result of transform. Matrix then can be used to transform
542    /// a vector, or combine with other matrix, to make transform hierarchy for example.
543    pub fn matrix(&self) -> Matrix4<f32> {
544        if self.dirty.get() {
545            self.matrix.set(self.calculate_local_transform());
546            self.dirty.set(false)
547        }
548        self.matrix.get()
549    }
550}
551
552/// Transform builder allows you to construct transform in declarative manner.
553/// This is typical implementation of Builder pattern.
554pub struct TransformBuilder {
555    local_scale: Vector3<f32>,
556    local_position: Vector3<f32>,
557    local_rotation: UnitQuaternion<f32>,
558    pre_rotation: UnitQuaternion<f32>,
559    post_rotation: UnitQuaternion<f32>,
560    rotation_offset: Vector3<f32>,
561    rotation_pivot: Vector3<f32>,
562    scaling_offset: Vector3<f32>,
563    scaling_pivot: Vector3<f32>,
564}
565
566impl Default for TransformBuilder {
567    fn default() -> Self {
568        Self::new()
569    }
570}
571
572impl TransformBuilder {
573    /// Creates new transform builder. If it won't be modified then it will produce
574    /// identity transform as result.
575    pub fn new() -> Self {
576        Self {
577            local_scale: Vector3::new(1.0, 1.0, 1.0),
578            local_position: Default::default(),
579            local_rotation: UnitQuaternion::identity(),
580            pre_rotation: UnitQuaternion::identity(),
581            post_rotation: UnitQuaternion::identity(),
582            rotation_offset: Default::default(),
583            rotation_pivot: Default::default(),
584            scaling_offset: Default::default(),
585            scaling_pivot: Default::default(),
586        }
587    }
588
589    /// Sets desired local scale.
590    pub fn with_local_scale(mut self, scale: Vector3<f32>) -> Self {
591        self.local_scale = scale;
592        self
593    }
594
595    /// Sets desired local position.
596    pub fn with_local_position(mut self, position: Vector3<f32>) -> Self {
597        self.local_position = position;
598        self
599    }
600
601    /// Sets desired local rotation.
602    pub fn with_local_rotation(mut self, rotation: UnitQuaternion<f32>) -> Self {
603        self.local_rotation = rotation;
604        self
605    }
606
607    /// Sets desired pre-rotation.
608    pub fn with_pre_rotation(mut self, rotation: UnitQuaternion<f32>) -> Self {
609        self.pre_rotation = rotation;
610        self
611    }
612
613    /// Sets desired post-rotation.
614    pub fn with_post_rotation(mut self, rotation: UnitQuaternion<f32>) -> Self {
615        self.post_rotation = rotation;
616        self
617    }
618
619    /// Sets desired rotation offset.
620    pub fn with_rotation_offset(mut self, offset: Vector3<f32>) -> Self {
621        self.rotation_offset = offset;
622        self
623    }
624
625    /// Sets desired rotation pivot.
626    pub fn with_rotation_pivot(mut self, pivot: Vector3<f32>) -> Self {
627        self.rotation_pivot = pivot;
628        self
629    }
630
631    /// Sets desired scaling offset.
632    pub fn with_scaling_offset(mut self, offset: Vector3<f32>) -> Self {
633        self.scaling_offset = offset;
634        self
635    }
636
637    /// Sets desired scaling pivot.
638    pub fn with_scaling_pivot(mut self, pivot: Vector3<f32>) -> Self {
639        self.scaling_pivot = pivot;
640        self
641    }
642
643    /// Builds new Transform instance using provided values.
644    pub fn build(self) -> Transform {
645        Transform {
646            dirty: Cell::new(true),
647            local_scale: self.local_scale.into(),
648            local_position: self.local_position.into(),
649            local_rotation: self.local_rotation.into(),
650            pre_rotation: self.pre_rotation.into(),
651            post_rotation: self.post_rotation.into(),
652            rotation_offset: self.rotation_offset.into(),
653            rotation_pivot: self.rotation_pivot.into(),
654            scaling_offset: self.scaling_offset.into(),
655            scaling_pivot: self.scaling_pivot.into(),
656            matrix: Cell::new(Matrix4::identity()),
657            post_rotation_matrix: build_post_rotation_matrix(self.post_rotation),
658        }
659    }
660}