Alchemy
Loading...
Searching...
No Matches

transform.c

Go to the documentation of this file.
1/// @file
2/// The 3D transform, its stack, the look-at matrix and the projection.
3/// Part of runtime.c, the runtime object, compiled only through it.
4
6
7// The 3D transform. gTransform is twelve s32 in 16.16 fixed point (1.0 is
8// 0x10000): [0] to [8] a 3x3 rotation, [9] to [11] a translation.
9// Iwram_TransformMatrix multiplies the matrix it is given into gTransform.
10
11#define TRANSFORM_DMA (DMA_COPY_WORDS(12)) ///< one matrix
12
13typedef s32 (*Dot3Fn)(s32 ax, s32 bx, s32 ay, s32 by, s32 az, s32 bz);
14
15/// Allocates the one-level save area (heap slot HEAP_SLOT_TRANSFORM_STACK),
16/// clears gTransformStackDepth and resets gTransform to identity. Called by
17/// the battle scene setup and the field camera before a scene's first
18/// transform.
23 CAMELOT_ASM("the fixed-register identity store of transform.h")
25}
26
27/// Saves gTransform into the save area (one level deep; a second save is
28/// ignored). Called by the battle effects that draw a part with its own
29/// transform, with Transform_Pop after.
30void Transform_Push(void) {
31 if (gTransformStackDepth <= 0) {
35 }
36}
37
38/// Copies gTransform to `dst`. Called by BattleFx_RunAtalanta.
39void Transform_Store(void *dst) {
41}
42
43/// Copies the matrix at `src` into gTransform. Called by BattleFx_RunAtalanta.
44void Transform_Load(const void *src) {
46}
47
48/// Restores gTransform from the save area, if Transform_Push saved it.
49void Transform_Pop(void) {
50 if (gTransformStackDepth > 0) {
54 }
55}
56
57/// Resets gTransform to identity. Reached through the far-call table.
59 CAMELOT_ASM("the fixed-register identity store of transform.h")
61}
62
63/// Rotates gTransform by angles[0], angles[1] and angles[2] about the X, Y and
64/// Z axes, in that order, with one combined matrix. Reached through the
65/// far-call table.
67 s32 sinX, cosX, sinY, cosY, sinZ, cosZ;
68 s32 matrix[12];
69
70 sinX = Trig_Sin(angles[0]);
71 cosX = Trig_Cos(angles[0]);
72 sinY = Trig_Sin(angles[1]);
73 cosY = Trig_Cos(angles[1]);
74 sinZ = Trig_Sin(angles[2]);
75 cosZ = Trig_Cos(angles[2]);
76 matrix[0] = Iwram_MulQ16(cosY, cosZ);
77 matrix[1] = Iwram_MulQ16(cosY, sinZ);
78 matrix[2] = -sinY;
79 matrix[3] =
80 Iwram_MulQ16(Iwram_MulQ16(sinX, sinY), cosZ) - Iwram_MulQ16(cosX, sinZ);
81 matrix[4] =
82 Iwram_MulQ16(Iwram_MulQ16(sinX, sinY), sinZ) + Iwram_MulQ16(cosX, cosZ);
83 matrix[5] = Iwram_MulQ16(sinX, cosY);
84 matrix[6] =
85 Iwram_MulQ16(Iwram_MulQ16(cosX, sinY), cosZ) + Iwram_MulQ16(sinX, sinZ);
86 matrix[7] =
87 Iwram_MulQ16(Iwram_MulQ16(cosX, sinY), sinZ) - Iwram_MulQ16(sinX, cosZ);
88 matrix[8] = Iwram_MulQ16(cosX, cosY);
89 matrix[9] = 0;
90 matrix[10] = 0;
91 matrix[11] = 0;
93}
94
95/// Rotates gTransform about the X axis by `angle`. Called by the battle and
96/// field cameras.
98 s32 matrix[12];
99 s32 sine = Trig_Sin(angle);
100 s32 cosine = Trig_Cos(angle);
101
102 CAMELOT_ASM("the fixed-register identity store of transform.h")
103 Transform_SetIdentity(matrix);
104 matrix[4] = cosine;
105 matrix[5] = sine;
106 matrix[7] = -sine;
107 matrix[8] = cosine;
108 Iwram_TransformMatrix(matrix);
109}
110
111/// Rotates gTransform about the Y axis by `angle`. Called by the battle and
112/// field cameras.
114 s32 matrix[12];
115 s32 sine = Trig_Sin(angle);
116 s32 cosine = Trig_Cos(angle);
117
118 CAMELOT_ASM("the fixed-register identity store of transform.h")
119 Transform_SetIdentity(matrix);
120 matrix[0] = cosine;
121 matrix[2] = -sine;
122 matrix[6] = sine;
123 matrix[8] = cosine;
124 Iwram_TransformMatrix(matrix);
125}
126
127/// Rotates gTransform about the Z axis by `angle`. Called by battle effects
128/// that spin their picture.
130 s32 matrix[12];
131 s32 sine = Trig_Sin(angle);
132 s32 cosine = Trig_Cos(angle);
133
134 CAMELOT_ASM("the fixed-register identity store of transform.h")
135 Transform_SetIdentity(matrix);
136 matrix[0] = cosine;
137 matrix[1] = sine;
138 matrix[3] = -sine;
139 matrix[4] = cosine;
140 Iwram_TransformMatrix(matrix);
141}
142
143/// Translates gTransform by `position` (x, y, z in 16.16). Called by the
144/// battle and field cameras and effects.
145void SceneTransform_ApplyPosition(const s32 *position) {
146 s32 matrix[12];
147
148 CAMELOT_ASM("the fixed-register identity store of transform.h")
149 Transform_SetIdentity(matrix);
150 matrix[9] = position[0];
151 matrix[10] = position[1];
152 matrix[11] = position[2];
153 Iwram_TransformMatrix(matrix);
154}
155
156/// Scales gTransform by `scale` (x, y, z factors in 16.16). Called by a few
157/// battle effects (Bind, Astral Blast, Thor).
158void SceneTransform_ApplyScale(const s32 *scale) {
159 s32 matrix[12];
160
161 CAMELOT_ASM("the fixed-register identity store of transform.h")
162 Transform_SetIdentity(matrix);
163 matrix[0] = scale[0];
164 matrix[4] = scale[1];
165 matrix[8] = scale[2];
166 Iwram_TransformMatrix(matrix);
167}
168
169/// Rotates gTransform by `angles` and translates it by `position` in one
170/// matrix. Reached through the far-call table.
172 s32 sinX, cosX, sinY, cosY, sinZ, cosZ;
173 s32 matrix[12];
174
175 sinX = Trig_Sin(angles[0]);
176 cosX = Trig_Cos(angles[0]);
177 sinY = Trig_Sin(angles[1]);
178 cosY = Trig_Cos(angles[1]);
179 sinZ = Trig_Sin(angles[2]);
180 cosZ = Trig_Cos(angles[2]);
181 matrix[0] = Iwram_MulQ16(cosY, cosZ);
182 matrix[1] = Iwram_MulQ16(cosY, sinZ);
183 matrix[2] = -sinY;
184 matrix[3] =
185 Iwram_MulQ16(Iwram_MulQ16(sinX, sinY), cosZ) - Iwram_MulQ16(cosX, sinZ);
186 matrix[4] =
187 Iwram_MulQ16(Iwram_MulQ16(sinX, sinY), sinZ) + Iwram_MulQ16(cosX, cosZ);
188 matrix[5] = Iwram_MulQ16(sinX, cosY);
189 matrix[6] =
190 Iwram_MulQ16(Iwram_MulQ16(cosX, sinY), cosZ) + Iwram_MulQ16(sinX, sinZ);
191 matrix[7] =
192 Iwram_MulQ16(Iwram_MulQ16(cosX, sinY), sinZ) - Iwram_MulQ16(sinX, cosZ);
193 matrix[8] = Iwram_MulQ16(cosX, cosY);
194 matrix[9] = position[0];
195 matrix[10] = position[1];
196 matrix[11] = position[2];
197 Iwram_TransformMatrix(matrix);
198}
199
200/// Rotates by `angles`, scales each rotation row by `scale` and translates by
201/// `position`, in one matrix.
203 s32 *scale) {
204 s32 sinX, cosX, sinY, cosY, sinZ, cosZ;
205 s32 axisScale;
206 s32 matrix[12];
207
208 sinX = Trig_Sin(angles[0]);
209 cosX = Trig_Cos(angles[0]);
210 sinY = Trig_Sin(angles[1]);
211 cosY = Trig_Cos(angles[1]);
212 sinZ = Trig_Sin(angles[2]);
213 cosZ = Trig_Cos(angles[2]);
214 axisScale = scale[0];
215 matrix[0] = Iwram_MulQ16(axisScale, Iwram_MulQ16(cosY, cosZ));
216 matrix[1] = Iwram_MulQ16(axisScale, Iwram_MulQ16(cosY, sinZ));
217 matrix[2] = Iwram_MulQ16(axisScale, -sinY);
218 axisScale = scale[1];
219 matrix[3] =
220 Iwram_MulQ16(axisScale, Iwram_MulQ16(Iwram_MulQ16(sinX, sinY), cosZ) -
221 Iwram_MulQ16(cosX, sinZ));
222 matrix[4] =
223 Iwram_MulQ16(axisScale, Iwram_MulQ16(Iwram_MulQ16(sinX, sinY), sinZ) +
224 Iwram_MulQ16(cosX, cosZ));
225 matrix[5] = Iwram_MulQ16(axisScale, Iwram_MulQ16(sinX, cosY));
226 axisScale = scale[2];
227 matrix[6] =
228 Iwram_MulQ16(axisScale, Iwram_MulQ16(Iwram_MulQ16(cosX, sinY), cosZ) +
229 Iwram_MulQ16(sinX, sinZ));
230 matrix[7] =
231 Iwram_MulQ16(axisScale, Iwram_MulQ16(Iwram_MulQ16(cosX, sinY), sinZ) -
232 Iwram_MulQ16(sinX, cosZ));
233 matrix[8] = Iwram_MulQ16(axisScale, Iwram_MulQ16(cosX, cosY));
234 matrix[9] = position[0];
235 matrix[10] = position[1];
236 matrix[11] = position[2];
237 Iwram_TransformMatrix(matrix);
238}
239
240/// Builds in `out` the camera matrix of an eye at `eye` looking at `target`.
241/// The camera's sideways axis is kept level (no Y component). Called by
242/// Transform_SetLookAt and Transform_ApplyLookAt.
243void Transform_BuildLookAt(s32 *eye, s32 *target, s32 *out) {
244 u32 code[7];
245 s32 dx, dy, dz;
246 s32 ux;
247 s32 zero = 0;
248 s32 ex;
249 Dot3Fn dot;
250 s32 ey;
251 s32 uz;
252 s32 vx, vy, vz;
253 s32 scale;
254 s32 ez;
255 s32 t;
256 u32 num = 0x80000000; // 2^31, the dividend of each reciprocal
257
258 // Copy the dot product routine to the stack and call it there
259 dot = (Dot3Fn)code;
261
262 // The view axis d, pointing from the target back to the eye, normalized
263 dx = target[0] - eye[0];
264 dy = target[1] - eye[1];
265 dz = target[2] - eye[2];
266 t = Iwram_Sqrt(dot(dx >> 8, dx >> 8, dy >> 8, dy >> 8, dz >> 8, dz >> 8));
267 scale = Iwram_UnsignedDivide(num, t) >> 15;
268 scale = -scale;
269 /* FAKEMATCH: the normalization keeps its CSE block, so the third product
270 * spills after the negated axis; without it, 4 instructions differ in
271 * tbs-en. */
272 do {
273 dx = Iwram_MulQ16(dx, scale);
274 dy = Iwram_MulQ16(dy, scale);
275 dz = Iwram_MulQ16(dz, scale);
276 uz = -dx;
277 } while (0);
278
279 // The sideways axis u = (dz, 0, -dx), normalized
280 t = Iwram_MulQ16(dy, dy);
281 scale = 0x10000;
282 if (scale - t > 0)
283 scale = Iwram_UnsignedDivide(num, FixedSqrt(scale - t)) << 1;
284 ux = Iwram_MulQ16(dz, scale);
285 uz = Iwram_MulQ16(uz, scale);
286
287 // The up axis v, the cross product of d and u, normalized
288 vx = Iwram_MulQ16(dy, uz);
289 vy = Iwram_MulQ16(dz, ux) - Iwram_MulQ16(dx, uz);
290 vz = -Iwram_MulQ16(dy, ux);
291 scale = Iwram_UnsignedDivide(num, FixedSqrt(dot(vx, vx, vy, vy, vz, vz)))
292 << 1;
293 vx = Iwram_MulQ16(vx, scale);
294 vy = Iwram_MulQ16(vy, scale);
295 vz = Iwram_MulQ16(vz, scale);
296
297 // The three axes are the matrix columns; the translation is each axis's
298 // dot product with the eye, negated
299 ex = eye[0];
300 ey = eye[1];
301 ez = eye[2];
302 out[0] = ux;
303 out[3] = zero;
304 out[6] = uz;
305 out[9] = -dot(ex, ux, ez, uz, zero, zero);
306 out[4] = vy;
307 out[7] = vz;
308 out[1] = vx;
309 out[10] = -dot(ex, vx, ey, vy, ez, vz);
310 out[2] = dx;
311 out[5] = dy;
312 out[8] = dz;
313 out[11] = -dot(ex, dx, ey, dy, ez, dz);
314}
315
316/// Makes the camera at `eye` looking at `target` the whole of gTransform.
317/// Called by the battle cameras.
318void Transform_SetLookAt(s32 *eye, s32 *target) {
319 Transform_BuildLookAt(eye, target, gTransform);
320}
321
322/// Multiplies the camera at `eye` looking at `target` into gTransform. Called
323/// by the battle and field object cameras.
324void Transform_ApplyLookAt(s32 *eye, s32 *target) {
325 s32 work[12];
326
327 Transform_BuildLookAt(eye, target, work);
329}
330
331/// Sets gProjection from a vertical field of view: the focal length is 80
332/// (half the screen height) over the tangent of half the angle. Points nearer
333/// than `nearDepth` or farther than `farDepth` are not drawn. Reached through
334/// the far-call table.
335void Projection_SetFieldOfView(u32 fieldOfView, s32 nearDepth, s32 farDepth) {
336 s32 halfAngle;
337 s32 sine;
338 s32 focal;
339
340 halfAngle = (s32)(fieldOfView + (fieldOfView >> 31)) >> 1;
341 sine = Trig_Sin(halfAngle);
342 focal = Iwram_RatioMulQ14(sine, Trig_Cos(halfAngle) * 80);
343 gProjection.near = nearDepth;
344 gProjection.focal = focal;
345 gProjection.far = farDepth;
346}
347
348/// Sets gProjection's focal length and depth range directly. Called by the
349/// battle cameras and effects.
350void Projection_Set(u32 focal, u32 nearDepth, u32 farDepth) {
351 struct Projection *projection = &gProjection;
352
353 projection->focal = focal;
354 projection->near = nearDepth;
355 projection->far = farDepth;
356}
357
358/// Reads three word-aligned coordinates from `point`, transforms them by
359/// gTransform and projects them to the screen: screen[0] and screen[1] are
360/// its pixel position and screen[2] its whole-unit depth. Returns the
361/// projection scale (16.16), or 0 when the point is outside the depth range,
362/// when only the transformed point is written. Called by the battle and field
363/// code that places sprites in 3D.
364s32 Render_ProjectPoint(const void *point, s32 *screen) {
365 struct Projection *projection;
366 s32 depth;
367 s32 scale;
368 s32 result;
369
370 Iwram_TransformVector(point, screen);
371 projection = &gProjection;
372 depth = -screen[2];
373 result = 0;
374 if (depth >= projection->near && depth <= projection->far) {
375 screen[2] = depth >> 16;
376 if (projection->focal != 0) {
377 u32 shifted = (u32)depth >> 11;
378
379 scale = Iwram_UnsignedDivide(projection->focal << 5, shifted);
380 } else {
381 scale = 0x151eb; // about 1.32
382 }
383 screen[0] = projection->centerX + Iwram_MulQ16(screen[0], scale) / 0x10000;
384 screen[1] = projection->centerY - Iwram_MulQ16(screen[1], scale) / 0x10000;
385 result = scale;
386 }
387 return result;
388}
389
390#undef TRANSFORM_DMA
static __inline__ void Dma_Set(const volatile void *source, volatile void *destination, u32 control, volatile u32 *channel)
Starts a DMA transfer: stores the source, destination and control words into channel's registers with...
Definition dma.h:62
s32 Trig_Cos(s32 angle)
s32 Trig_Sin(s32 angle)
const u32 RelocatedQ16DotProduct[]
void * Runtime_AllocateBlock(s32 kind, s32 size)
As Runtime_AllocateHeapBlock, but tries EWRAM first, for blocks that need room more than speed: the b...
Definition heap.c:80
@ HEAP_SLOT_TRANSFORM_STACK
The 3D transform's one saved level.
Definition heap_state.h:34
#define REG_DMA3
Definition io_reg.h:153
#define DMA_COPY_WORDS(n)
Definition io_reg.h:226
static s32 Iwram_RatioMulQ14(s32 divisor, s32 value)
All calls go through this layer.
Definition iwram_call.h:51
static s32 Iwram_Sqrt(s32 value)
Definition iwram_call.h:63
#define Iwram_MulQ16(left, right)
Definition iwram_call.h:127
static s32 Iwram_TransformVector(const void *vector, s32 *result)
Definition iwram_call.h:65
static u32 Iwram_UnsignedDivide(u32 dividend, u32 divisor)
Definition iwram_call.h:77
static void Iwram_TransformMatrix(const s32 *matrix)
Definition iwram_call.h:69
s32 FixedSqrt(s32 value)
Square root of a 16.16 fixed-point value, in 16.16.
Definition math.c:149
struct Projection gProjection
What every part of the runtime object shares: its includes and the library call two parts make.
The 3D projection onto the screen: its focal length, near and far depths, and the screen point the vi...
Definition project.h:12
s32 centerX
screen pixels
Definition project.h:16
s32 centerY
screen pixels
Definition project.h:17
s32 near
Definition project.h:14
s32 far
Definition project.h:15
s32 focal
Definition project.h:13
void SceneTransform_ApplyYaw(s32 angle)
Rotates gTransform about the Y axis by angle.
Definition transform.c:113
void Projection_Set(u32 focal, u32 nearDepth, u32 farDepth)
Sets gProjection's focal length and depth range directly.
Definition transform.c:350
void Transform_Load(const void *src)
Copies the matrix at src into gTransform. Called by BattleFx_RunAtalanta.
Definition transform.c:44
s32 Render_ProjectPoint(const void *point, s32 *screen)
Reads three word-aligned coordinates from point, transforms them by gTransform and projects them to t...
Definition transform.c:364
void SceneTransform_ApplyPitch(s32 angle)
Rotates gTransform about the X axis by angle.
Definition transform.c:97
void SceneTransform_ResetMatrix(void)
Resets gTransform to identity. Reached through the far-call table.
Definition transform.c:58
void Transform_Pop(void)
Restores gTransform from the save area, if Transform_Push saved it.
Definition transform.c:49
void Projection_SetFieldOfView(u32 fieldOfView, s32 nearDepth, s32 farDepth)
Sets gProjection from a vertical field of view: the focal length is 80 (half the screen height) over ...
Definition transform.c:335
void SceneTransform_ApplyPosition(const s32 *position)
Translates gTransform by position (x, y, z in 16.16).
Definition transform.c:145
#define TRANSFORM_DMA
one matrix
Definition transform.c:11
void Transform_Push(void)
Saves gTransform into the save area (one level deep; a second save is ignored).
Definition transform.c:30
void Transform_ApplyLookAt(s32 *eye, s32 *target)
Multiplies the camera at eye looking at target into gTransform.
Definition transform.c:324
void Render_ResetTransformState(void)
Allocates the one-level save area (heap slot HEAP_SLOT_TRANSFORM_STACK), clears gTransformStackDepth ...
Definition transform.c:19
s32(*) Dot3Fn(s32 ax, s32 bx, s32 ay, s32 by, s32 az, s32 bz)
Definition transform.c:13
void Transform_Store(void *dst)
Copies gTransform to dst. Called by BattleFx_RunAtalanta.
Definition transform.c:39
void SceneTransform_ApplyScale(const s32 *scale)
Scales gTransform by scale (x, y, z factors in 16.16).
Definition transform.c:158
void Transform_SetLookAt(s32 *eye, s32 *target)
Makes the camera at eye looking at target the whole of gTransform.
Definition transform.c:318
void Transform_BuildLookAt(s32 *eye, s32 *target, s32 *out)
Builds in out the camera matrix of an eye at eye looking at target.
Definition transform.c:243
void SceneTransform_ApplyRotationTranslation(s32 *angles, s32 *position)
Rotates gTransform by angles and translates it by position in one matrix.
Definition transform.c:171
void SceneTransform_ApplyRoll(s32 angle)
Rotates gTransform about the Z axis by angle.
Definition transform.c:129
void SceneTransform_ApplyRotation(s32 *angles)
Rotates gTransform by angles[0], angles[1] and angles[2] about the X, Y and Z axes,...
Definition transform.c:66
void SceneTransform_ApplyScaledRotation(s32 *angles, s32 *position, s32 *scale)
Rotates by angles, scales each rotation row by scale and translates by position, in one matrix.
Definition transform.c:202
static __inline__ void Transform_SetIdentity(void *matrix)
Definition transform.h:17
s32 gTransform[12]
u8 * gTransformStackTop
static __inline__ void Transform_ResetCurrent(void)
Resets the current matrix to identity.
Definition transform.h:36
s32 gTransformStackDepth
signed int s32
Definition types.h:14
unsigned int u32
Definition types.h:15
#define CAMELOT_ASM(proof)
Definition types.h:46