1 /**
2 3D Transformation. 3x4 matrix.
3 
4 Copyright:
5 Copyright (c) 2007 Juan Linietsky, Ariel Manzur.
6 Copyright (c) 2014 Godot Engine contributors (cf. AUTHORS.md)
7 Copyright (c) 2017 Godot-D contributors
8 Copyright (c) 2022 Godot-DLang contributors
9 
10 License: $(LINK2 https://opensource.org/licenses/MIT, MIT License)
11 
12 
13 */
14 module godot.transform;
15 
16 import godot.api.types;
17 import godot.vector3;
18 import godot.quat;
19 import godot.basis;
20 import godot.aabb;
21 import godot.plane;
22 
23 /**
24 Represents one or many transformations in 3D space such as translation, rotation, or scaling. It is similar to a 3x4 matrix.
25 */
26 struct Transform3D {
27 @nogc nothrow:
28 
29     Basis basis; /// 
30     Vector3 origin; /// 
31 
32     this(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz,
33         real_t zx, real_t zy, real_t zz, real_t tx, real_t ty, real_t tz) {
34         set(xx, xy, xz, yx, yy, yz, zx, zy, zz, tx, ty, tz);
35     }
36 
37     this(in Basis basis, in Vector3 origin) {
38         this.basis = basis;
39         this.origin = origin;
40     }
41 
42     Transform3D inverseXform(in Transform3D t) const {
43         Vector3 v = t.origin - origin;
44         return Transform3D(basis.transposeXform(t.basis),
45             basis.xform(v));
46     }
47 
48     void set(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz, real_t tx, real_t ty, real_t tz) {
49         basis.elements[0][0] = xx;
50         basis.elements[0][1] = xy;
51         basis.elements[0][2] = xz;
52         basis.elements[1][0] = yx;
53         basis.elements[1][1] = yy;
54         basis.elements[1][2] = yz;
55         basis.elements[2][0] = zx;
56         basis.elements[2][1] = zy;
57         basis.elements[2][2] = zz;
58         origin.x = tx;
59         origin.y = ty;
60         origin.z = tz;
61     }
62 
63     Vector3 xform(in Vector3 p_vector) const {
64         return Vector3(
65             basis[0].dot(p_vector) + origin.x,
66             basis[1].dot(p_vector) + origin.y,
67             basis[2].dot(p_vector) + origin.z
68         );
69     }
70 
71     Vector3 xformInv(in Vector3 p_vector) const {
72         Vector3 v = p_vector - origin;
73 
74         return Vector3(
75             (basis.elements[0][0] * v.x) + (basis.elements[1][0] * v.y) + (
76                 basis.elements[2][0] * v.z),
77             (basis.elements[0][1] * v.x) + (basis.elements[1][1] * v.y) + (
78                 basis.elements[2][1] * v.z),
79             (basis.elements[0][2] * v.x) + (basis.elements[1][2] * v.y) + (
80                 basis.elements[2][2] * v.z)
81         );
82     }
83 
84     Plane xform(in Plane p_plane) const {
85         Vector3 point = p_plane.normal * p_plane.d;
86         Vector3 point_dir = point + p_plane.normal;
87         point = xform(point);
88         point_dir = xform(point_dir);
89 
90         Vector3 normal = point_dir - point;
91         normal.normalize();
92         real_t d = normal.dot(point);
93 
94         return Plane(normal, d);
95 
96     }
97 
98     Plane xformInv(in Plane p_plane) const {
99         Vector3 point = p_plane.normal * p_plane.d;
100         Vector3 point_dir = point + p_plane.normal;
101         point = xformInv(point);
102         point_dir = xformInv(point_dir);
103 
104         Vector3 normal = point_dir - point;
105         normal.normalize();
106         real_t d = normal.dot(point);
107 
108         return Plane(normal, d);
109 
110     }
111 
112     AABB xform(in AABB p_aabb) const {
113         /* define vertices */
114         Vector3 x = basis.getAxis(0) * p_aabb.size.x;
115         Vector3 y = basis.getAxis(1) * p_aabb.size.y;
116         Vector3 z = basis.getAxis(2) * p_aabb.size.z;
117         Vector3 pos = xform(p_aabb.position);
118         //could be even further optimized
119         AABB new_aabb;
120         new_aabb.position = pos;
121         new_aabb.expandTo(pos + x);
122         new_aabb.expandTo(pos + y);
123         new_aabb.expandTo(pos + z);
124         new_aabb.expandTo(pos + x + y);
125         new_aabb.expandTo(pos + x + z);
126         new_aabb.expandTo(pos + y + z);
127         new_aabb.expandTo(pos + x + y + z);
128         return new_aabb;
129     }
130 
131     AABB xformInv(in AABB p_aabb) const {
132         /* define vertices */
133         Vector3[8] vertices = [
134             Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z + p_aabb
135                     .size.z),
136             Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y + p_aabb.size.y, p_aabb
137                     .position.z),
138             Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y, p_aabb.position.z + p_aabb
139                     .size.z),
140             Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y, p_aabb.position.z),
141             Vector3(p_aabb.position.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z + p_aabb
142                     .size.z),
143             Vector3(p_aabb.position.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z),
144             Vector3(p_aabb.position.x, p_aabb.position.y, p_aabb.position.z + p_aabb.size.z),
145             Vector3(p_aabb.position.x, p_aabb.position.y, p_aabb.position.z)
146         ];
147         AABB ret;
148         ret.position = xformInv(vertices[0]);
149         for (int i = 1; i < 8; i++) {
150             ret.expandTo(xformInv(vertices[i]));
151         }
152         return ret;
153 
154     }
155 
156     void affineInvert() {
157         basis.invert();
158         origin = basis.xform(-origin);
159     }
160 
161     Transform3D affineInverse() const {
162         Transform3D ret = this;
163         ret.affineInvert();
164         return ret;
165 
166     }
167 
168     void invert() {
169         basis.transpose();
170         origin = basis.xform(-origin);
171     }
172 
173     Transform3D inverse() const {
174         // FIXME: this function assumes the basis is a rotation matrix, with no scaling.
175         // affine_inverse can handle matrices with scaling, so GDScript should eventually use that.
176         Transform3D ret = this;
177         ret.invert();
178         return ret;
179     }
180 
181     void rotate(in Vector3 p_axis, real_t p_phi) {
182         this = rotated(p_axis, p_phi);
183     }
184 
185     Transform3D rotated(in Vector3 p_axis, real_t p_phi) const {
186         return Transform3D(Basis(p_axis, p_phi), Vector3()) * (this);
187     }
188 
189     void rotateBasis(in Vector3 p_axis, real_t p_phi) {
190         basis.rotate(p_axis, p_phi);
191     }
192 
193     Transform3D lookingAt(in Vector3 p_target, in Vector3 p_up) const {
194         Transform3D t = this;
195         t.setLookAt(origin, p_target, p_up);
196         return t;
197     }
198 
199     void setLookAt(in Vector3 p_eye, in Vector3 p_target, in Vector3 p_up) {
200         // Reference: MESA source code
201         Vector3 v_x, v_y, v_z;
202         /* Make rotation matrix */
203 
204         /* Z vector */
205         v_z = p_eye - p_target;
206 
207         v_z.normalize();
208 
209         v_y = p_up;
210 
211         v_x = v_y.cross(v_z);
212 
213         /* Recompute Y = Z cross X */
214         v_y = v_z.cross(v_x);
215 
216         v_x.normalize();
217         v_y.normalize();
218 
219         basis.setAxis(0, v_x);
220         basis.setAxis(1, v_y);
221         basis.setAxis(2, v_z);
222         origin = p_eye;
223     }
224 
225     Transform3D interpolateWith(in Transform3D p_transform, real_t p_c) const {
226         /* not sure if very "efficient" but good enough? */
227         Vector3 src_scale = basis.getScale();
228         Quaternion src_rot = basis.quat;
229         Vector3 src_loc = origin;
230 
231         Vector3 dst_scale = p_transform.basis.getScale();
232         Quaternion dst_rot = p_transform.basis.quat;
233         Vector3 dst_loc = p_transform.origin;
234 
235         Transform3D dst;
236         dst.basis = Basis(src_rot.slerp(dst_rot, p_c));
237         dst.basis.scale(src_scale.linearInterpolate(dst_scale, p_c));
238         dst.origin = src_loc.linearInterpolate(dst_loc, p_c);
239 
240         return dst;
241     }
242 
243     void scale(in Vector3 p_scale) {
244         basis.scale(p_scale);
245         origin *= p_scale;
246     }
247 
248     Transform3D scaled(in Vector3 p_scale) const {
249         Transform3D t = this;
250         t.scale(p_scale);
251         return t;
252     }
253 
254     void scaleBasis(in Vector3 p_scale) {
255         basis.scale(p_scale);
256     }
257 
258     void translate(real_t p_tx, real_t p_ty, real_t p_tz) {
259         translate(Vector3(p_tx, p_ty, p_tz));
260     }
261 
262     void translate(in Vector3 p_translation) {
263         for (int i = 0; i < 3; i++) {
264             origin[i] += basis[i].dot(p_translation);
265         }
266     }
267 
268     Transform3D translated(in Vector3 p_translation) const {
269         Transform3D t = this;
270         t.translate(p_translation);
271         return t;
272     }
273 
274     void orthonormalize() {
275         basis.orthonormalize();
276     }
277 
278     Transform3D orthonormalized() const {
279         Transform3D _copy = this;
280         _copy.orthonormalize();
281         return _copy;
282     }
283 
284     void opOpAssign(string op : "*")(in Transform3D p_transform) {
285         origin = xform(p_transform.origin);
286         basis *= p_transform.basis;
287     }
288 
289     Transform3D opBinary(string op : "*")(in Transform3D p_transform) const {
290         Transform3D t = this;
291         t *= p_transform;
292         return t;
293     }
294 }