Paper Mario DX
Paper Mario (N64) modding
 
Loading...
Searching...
No Matches
cam_math.c
Go to the documentation of this file.
1#include "common.h"
2#include "camera.h"
3#include <stdlib.h>
4
6
9 s32 zoneID;
10
11 hitDepth = 32767.0f;
12 zoneID = test_ray_zones(posX, posY, posZ, 0.0f, -1.0f, 0.0f, &hitX, &hitY, &hitZ, &hitDepth, &nX, &nY, &nZ);
13 if (zoneID >= 0) {
14 if (zone != NULL) {
16 }
17 return gZoneCollisionData.colliderList[zoneID].camSettings;
18 } else {
19 return NULL;
20 }
21}
22
26 f32 disc;
27
28 f32 alpha;
29 f32 Px;
30 f32 Pz;
31
32 f32 dx12 = A2x - A1x;
33 f32 dz12 = A2z - A1z;
34 f32 dx13 = B1x - A1x;
35 f32 dz13 = B1z - A1z;
36 f32 dx14 = B2x - A1x;
37 f32 dz14 = B2z - A1z;
38 f32 dx34 = B2x - B1x;
39 f32 dz34 = B2z - B1z;
40
41 // if distance between points on the line is 0
42 if (dx12 == 0.0f && dz12 == 0.0f) {
43 return FALSE;
44 }
45 // if length of second segment is 0
46 if (dx34 == 0.0f && dz34 == 0.0f) {
47 return FALSE;
48 }
49
50 disc = -dz12 * dx13 + dx12 * dz13;
51 if (disc < 0.0f) {
52 B1_side = -1.0f;
53 } else if (disc > 0.0f) {
54 B1_side = 1.0f;
55 } else {
56 B1_side = 0.0f;
57 }
58
59 disc = -dz12 * dx14 + dx12 * dz14;
60 if (disc < 0.0f) {
61 B2_side = -1.0f;
62 } else if (disc > 0.0f) {
63 B2_side = 1.0f;
64 } else {
65 B2_side = 0.0f;
66 }
67
68 // B1 and B2 are on the same side relative to the line: no intersection
69 if (B1_side == B2_side) {
70 return FALSE;
71 }
72
73 if (fabsf(dx12) > fabsf(dx34)) {
74 /*
75 We represent intersection point P as P = B1 + alpha * (B2 - B1)
76 and solve the equation (P - A1) x (A2 - A1) = 0, where 'x' is cross product, A1 and A2 are points on the line
77 and B1 and B2 are the ends of the segment.
78 So, (B1 - A1 + alpha * (B2 - B1)) x (A2 - A1) = 0,
79 alpha = [-(B1 - A1) x (A2 - A1)] / [(B2 - B1) x (A2 - A1)]
80 */
81 // same as -(B1 - A1) x (A2 - A1)
82 alpha = A1x * dz12 - A1z * dx12 + dx12 * B1z - dz12 * B1x;
83 // divide by (B2 - B1) x (A2 - A1)
84 alpha /= dz12 * dx34 - dx12 * dz34;
85 /*
86 Now we represent P as P = A1 + beta * (A2 - A1), and we are to find beta.
87 B1 + alpha * (B2 - B1) = A1 + beta * (A2 - A1)
88 beta * (A2 - A1) = B1 + alpha * (B2 - B1) - A1
89 We use only 'x' part of this equation to find beta.
90
91 Actually this step could be omitted and we calculate intersection directly as B1 + alpha * (B2 - B1).
92 Don't know why it's done this way.
93 */
94 alpha = (B1x + dx34 * alpha - A1x) / dx12;
95 Px = A1x + dx12 * alpha;
96 Pz = A1z + dz12 * alpha;
97 } else {
98 /*
99 We represent intersection point P as P = A1 + alpha * (A2 - A1)
100 and solve the equation (B2 - B1) x (P - B1) = 0
101 */
102 // same as (B2 - B1) x (B1 - A1)
103 alpha = B1z * dx34 + A1x * dz34 - B1z * dz34 - A1z * dx34;
104 // divide by (B2 - B1) x (A2 - A1)
105 alpha /= dz12 * dx34 - dx12 * dz34;
106 // Now we represent P as P = B1 + beta * (B2 - B1) and find beta
107 alpha = (A1x + dx12 * alpha - B1x) / dx34;
108 Px = B1x + dx34 * alpha;
109 Pz = B1z + dz34 * alpha;
110 }
111 // (P - A1) * (P - A2) > 0 when P is outside of segment A1-A2
112 if ((Px - A1x) * (Px - A2x) + (Pz - A1z) * (Pz - A2z) > 0.0f) {
113 return FALSE;
114 }
115
116 *interX = Px;
117 *interZ = Pz;
118 // distance between P and B1
119 *squared_dist = SQ(Px - B1x) + SQ(Pz - B1z);
120 return TRUE;
121}
122
124 f32 B1_side;
125 f32 B2_side;
126 f32 disc;
127
128 f32 alpha;
129 f32 Px;
130 f32 Pz;
131
132 f32 dx12 = A2x - A1x;
133 f32 dz12 = A2z - A1z;
134 f32 dx13 = B1x - A1x;
135 f32 dz13 = B1z - A1z;
136 f32 dx14 = B2x - A1x;
137 f32 dz14 = B2z - A1z;
138 f32 dx34 = B2x - B1x;
139 f32 dz34 = B2z - B1z;
140
141 if (dx12 == 0.0f && dz12 == 0.0f) {
142 return FALSE;
143 }
144 if (dx34 == 0.0f && dz34 == 0.0f) {
145 return FALSE;
146 }
147
148 disc = -dz12 * dx13 + dx12 * dz13;
149 if (disc < 0.0f) {
150 B1_side = -1.0f;
151 } else if (disc > 0.0f) {
152 B1_side = 1.0f;
153 } else {
154 B1_side = 0.0f;
155 }
156
157 disc = -dz12 * dx14 + dx12 * dz14;
158 if (disc < 0.0f) {
159 B2_side = -1.0f;
160 } else if (disc > 0.0f) {
161 B2_side = 1.0f;
162 } else {
163 B2_side = 0.0f;
164 }
165
166 if (B1_side == B2_side) {
167 return FALSE;
168 }
169
170 if (fabsf(dx12) > fabsf(dx34)) {
171 alpha = A1x * dz12 - A1z * dx12 + dx12 * B1z - dz12 * B1x;
172 alpha /= dz12 * dx34 - dx12 * dz34;
173 alpha = (B1x + dx34 * alpha - A1x) / dx12;
174 Px = A1x + dx12 * alpha;
175 Pz = A1z + dz12 * alpha;
176 } else {
177 alpha = B1z * dx34 + A1x * dz34 - B1z * dz34 - A1z * dx34;
178 alpha /= dz12 * dx34 - dx12 * dz34;
179 alpha = (A1x + dx12 * alpha - B1x) / dx34;
180 Px = B1x + dx34 * alpha;
181 Pz = B1z + dz34 * alpha;
182 }
183
184 *interX = Px;
185 *interZ = Pz;
186 *squared_dist = SQ(Px - B1x) + SQ(Pz - B1z);
187 return TRUE;
188}
189
193
194 if (camSettings == NULL) {
195 return 0;
196 }
197 if (camSettings->type != CAM_CONTROL_CONSTAIN_BETWEEN_POINTS) {
198 return 0;
199 }
200
201 f32 Ax = camSettings->points.two.Ax;
202 f32 Az = camSettings->points.two.Az;
203 f32 Bx = camSettings->points.two.Bx;
204 f32 Bz = camSettings->points.two.Bz;
205
206 // dot product of AB and AP
207 dot1x = (Bx - Ax) * (Px - Ax);
208 dot1z = (Bz - Az) * (Pz - Az);
209 product1 = dot1x + dot1z;
210
211 // dot product of AB and BP
212 dot2x = (Bx - Ax) * (Px - Bx);
213 dot2z = (Bz - Az) * (Pz - Bz);
214 product2 = dot2x + dot2z;
215
216 if (product1 < 0 && product2 < 0) {
217 return -1;
218 }
219 if (product1 > 0 && product2 > 0) {
220 return 1;
221 }
222 return 0;
223}
224
227 f32 stickX;
229
230 if (camera->curSettings != NULL && camera->curSettings->type == CAM_CONTROL_FIXED_POS_AND_ORIENTATION) {
232 }
233
234 if (ignoreStickInput) {
235 stickX = 0.0f;
236 camera->increasingLeadInterp = TRUE;
237 camera->leadInterpAlpha = 1.0f;
238 camera->targetLeadAmount = 0.0f;
239 } else {
241 stickX = gPartnerStatus.stickX;
242 } else {
243 stickX = gPlayerStatusPtr->stickAxis[0];
244 }
245 if (stickX > 0.0f) {
246 stickX = 50.0f;
247 }
248 if (stickX < 0.0f) {
249 stickX = -50.0f;
250 }
251 }
252
253 if (stickX != 0.0f) {
254 if (stickX < 0.0f) {
255 if (camera->accumulatedStickLead > 0.0f) {
256 // reversing direction
257 camera->accumulatedStickLead = stickX;
258 } else {
259 camera->accumulatedStickLead += stickX;
260 }
261 if (camera->accumulatedStickLead <= -300.0f) {
262 // max accumulation
263 camera->increasingLeadInterp = TRUE;
264 if (camera->targetLeadAmount > 0.0f) {
265 camera->leadInterpAlpha = 0.0f;
266 }
267 camera->targetLeadAmount = -candidateLeadAmount;
268 camera->accumulatedStickLead = -300.0f;
269 }
270 } else {
271 if (camera->accumulatedStickLead < 0.0f) {
272 // reversing direction
273 camera->accumulatedStickLead = stickX;
274 } else {
275 camera->accumulatedStickLead += stickX;
276 }
277 if (camera->accumulatedStickLead >= 300.0f) {
278 // max accumulation
279 camera->increasingLeadInterp = TRUE;
280 if (camera->targetLeadAmount < 0.0f) {
281 camera->leadInterpAlpha = 0.0f;
282 }
283 camera->targetLeadAmount = candidateLeadAmount;
284 camera->accumulatedStickLead = 300.0f;
285 }
286 }
287 }
288
289 if (camera->increasingLeadInterp) {
290 camera->leadInterpAlpha += 0.01f;
291 if (camera->leadInterpAlpha > 1.0f) {
292 camera->leadInterpAlpha = 1.0f;
293 }
294 }
295
296 // determine ratio to interp leadAmount by
297 if (camera->targetLeadAmount - camera->leadAmount == 0.0f) {
298 camera->leadInterpAlpha = 0.0f;
299 camera->increasingLeadInterp = FALSE;
300 }
301
302 deltaLeadAmount = (camera->targetLeadAmount - camera->leadAmount) * camera->leadInterpAlpha;
303 if (camera->targetLeadAmount - camera->leadAmount > 0.0f) {
304 // snap small changes
305 if (camera->targetLeadAmount - camera->leadAmount < 0.1) {
306 deltaLeadAmount = camera->targetLeadAmount - camera->leadAmount;
307 }
308 // clamp large changes to 3.0
309 if (deltaLeadAmount > 3.0f) {
310 deltaLeadAmount = 3.0f;
311 }
312 } else {
313 // snap small changes
314 if (camera->targetLeadAmount - camera->leadAmount > -0.1) {
315 deltaLeadAmount = camera->targetLeadAmount - camera->leadAmount;
316 }
317 // clamp large changes to -3.0
318 if (deltaLeadAmount < -3.0f) {
319 deltaLeadAmount = -3.0f;
320 }
321 }
322
323 if (stickX != 0.0f || ignoreStickInput) {
324 camera->leadAmount += deltaLeadAmount;
325 } else {
326 camera->leadInterpAlpha = 0.0f;
327 }
328}
329
331 CameraControlSettings* settings;
332 Collider* zone;
333 f32 leadAmount;
334 s32 s2;
335
336 // check settings directly under the target position
337 settings = test_ray_zone(camera->targetPos.x, camera->targetPos.y + 10.0f, camera->targetPos.z, NULL);
338
339 leadAmount = camera->leadAmount;
340 s2 = 0;
341
342 if (settings != NULL) {
343 if (settings->type == CAM_CONTROL_CONSTRAIN_TO_LINE
345 || (s2 = func_800328A4(settings, camera->targetPos.x, camera->targetPos.z)) != 0
346 ) {
347 if (camera->needsInitialConstrainDir) {
348 f32 X, Y, Z, W;
349
350 guPerspectiveF(camera->mtxPerspective, &camera->perspNorm, camera->vfov,
351 (f32)camera->viewportW / (f32)camera->viewportH, camera->nearClip, camera->farClip, 1.0f);
352 guMtxCatF(camera->mtxViewPlayer, camera->mtxPerspective, camera->mtxPerspective);
353 transform_point(camera->mtxPerspective, camera->targetPos.x, camera->targetPos.y, camera->targetPos.z,
354 1.0f, &X, &Y, &Z, &W);
355 if (W == 0.0f) {
356 W = 1.0f;
357 }
358 W = 1.0f / W;
359 X *= W;
360 camera->leadConstrainDir = (X > 0.0f) ? 1 : (X < 0.0f) ? -1 : 0;
361 camera->needsInitialConstrainDir = FALSE;
362 } else {
363 CameraControlSettings* leadSettings = camera->prevLeadSettings;
364
365 if (leadSettings == NULL
368 || func_800328A4(settings, camera->prevLeadPosX, camera->prevLeadPosZ) != 0)
369 ) {
370 if (leadSettings != NULL && s2 != 0) {
371 camera->leadConstrainDir = s2;
372 } else {
373 f32 dx = camera->prevLeadPosX - camera->targetPos.x;
374 f32 dz = camera->prevLeadPosZ - camera->targetPos.z;
375 f32 cosYaw = cos_deg(camera->curYaw);
376 f32 sinYaw = sin_deg(camera->curYaw);
377 f32 product = dx * cosYaw + dz * sinYaw;
378 camera->leadConstrainDir = (product > 0) ? -1 : (product < 0) ? 1 : 0;
379 }
380 }
381 }
382
383 if (leadAmount > 0.0f && camera->leadConstrainDir > 0 || leadAmount < 0.0f && camera->leadConstrainDir < 0) {
384 camera->leadInterpAlpha = 0.0f;
385 camera->leadAmount = 0.0f;
386 }
387 camera->prevLeadSettings = settings;
388 camera->prevLeadPosX = camera->targetPos.x;
389 camera->prevLeadPosZ = camera->targetPos.z;
390 return;
391 }
392 }
393
394 camera->leadConstrainDir = 0;
395 camera->prevLeadSettings = settings;
396 camera->prevLeadPosX = camera->targetPos.x;
397 camera->prevLeadPosZ = camera->targetPos.z;
398
399 // find settings under (target + lead) position
400 f32 yaw = DEG_TO_RAD(camera->curBoomYaw);
401 f32 newPosX = camera->targetPos.x + leadAmount * cos_rad(yaw);
402 f32 newPosZ = camera->targetPos.z + leadAmount * sin_rad(yaw);
403 f32 newPosY = camera->targetPos.y + 10.0f;
404
405 settings = test_ray_zone(newPosX, newPosY, newPosZ, &zone);
406 if (settings == NULL) {
407 return;
408 }
409
410 if (settings->type == CAM_CONTROL_CONSTRAIN_TO_LINE
412 || func_800328A4(camera->prevLeadSettings, newPosX, newPosZ) != 0
413 ) {
415 f32 minDistSq = SQ(1000.0f);
417
418 // clamp lead amount to the points when using CAM_CONTROL_CONSTAIN_BETWEEN_POINTS
419 if (camera->prevLeadSettings != NULL && camera->prevLeadSettings->type == CAM_CONTROL_CONSTAIN_BETWEEN_POINTS) {
420 settings = camera->prevLeadSettings;
422
423 f32 ABx = settings->points.two.Bx - settings->points.two.Ax;
424 f32 ABz = settings->points.two.Bz - settings->points.two.Az;
425
426 if (calculate_line_segment_intersection(settings->points.two.Ax, settings->points.two.Az,
427 settings->points.two.Ax - ABz, settings->points.two.Az + ABx,
428 camera->targetPos.x, camera->targetPos.z, newPosX, newPosZ, &intX, &intZ, &intDistSq)
430 ) {
432 }
433 if (calculate_line_segment_intersection(settings->points.two.Bx, settings->points.two.Bz,
434 settings->points.two.Bx - ABz, settings->points.two.Bz + ABx,
435 camera->targetPos.x, camera->targetPos.z, newPosX, newPosZ, &intX, &intZ, &intDistSq)
437 ) {
439 }
440 }
441
443 for (s32 i = 0; i < zone->numTriangles; i++) {
444 if (calculate_segment_intersection(zone->triangleTable[i].v1->x, zone->triangleTable[i].v1->z,
445 zone->triangleTable[i].v2->x, zone->triangleTable[i].v2->z,
446 camera->targetPos.x, camera->targetPos.z, newPosX, newPosZ, &intX, &intZ, &intDistSq)
448 ) {
450 }
451 if (calculate_segment_intersection(zone->triangleTable[i].v2->x, zone->triangleTable[i].v2->z,
452 zone->triangleTable[i].v3->x, zone->triangleTable[i].v3->z,
453 camera->targetPos.x, camera->targetPos.z, newPosX, newPosZ, &intX, &intZ, &intDistSq)
455 ) {
457 }
458 if (calculate_segment_intersection(zone->triangleTable[i].v3->x, zone->triangleTable[i].v3->z,
459 zone->triangleTable[i].v1->x, zone->triangleTable[i].v1->z,
460 camera->targetPos.x, camera->targetPos.z, newPosX, newPosZ, &intX, &intZ, &intDistSq)
462 ) {
464 }
465 }
466 }
467
468 if (minDistSq == SQ(1000.0f) || minDistSq == 0.0f) {
469 camera->leadAmount = 0.0f;
470 } else {
471 camera->leadAmount = abs(camera->leadAmount > 0.0f) ? sqrtf(minDistSq) : -sqrtf(minDistSq);
472 }
473 camera->leadInterpAlpha = 0.0f;
474 }
475}
476
478 f32 dx = camera->lookAt_eye.x - camera->lookAt_obj.x;
479 f32 dy = camera->lookAt_eye.y - camera->lookAt_obj.y;
480 f32 dz = camera->lookAt_eye.z - camera->lookAt_obj.z;
481 f32 dist = sqrtf(SQ(dx) + SQ(dy) + SQ(dz));
482 f32 theta = ((camera->vfov * 0.5f) / 180.0f) * PI;
483 f32 distTanTheta = dist * (sin_rad(theta) / cos_rad(theta));
484 f32 hfov = distTanTheta * camera->viewportW / camera->viewportH;
485
488 guTranslateF(camera->mtxViewLeading, -camera->leadAmount, 0.0f, 0.0f);
489}
BSS s32 PopupMenu_SelectedIndex
s32 calculate_segment_intersection(f32 A1x, f32 A1z, f32 A2x, f32 A2z, f32 B1x, f32 B1z, f32 B2x, f32 B2z, f32 *interX, f32 *interZ, f32 *squared_dist)
Definition cam_math.c:23
void apply_constraints_to_lead_amount(Camera *camera)
Definition cam_math.c:330
void update_camera_lead_amount(Camera *camera, f32 candidateLeadAmount)
Definition cam_math.c:225
CameraControlSettings * test_ray_zone(f32 posX, f32 posY, f32 posZ, Collider **zone)
Definition cam_math.c:7
void create_camera_leadplayer_matrix(Camera *camera)
Definition cam_math.c:477
s32 func_800328A4(CameraControlSettings *camSettings, f32 Px, f32 Pz)
Definition cam_math.c:190
s32 gCurrentCameraID
Definition cam_math.c:5
s32 calculate_line_segment_intersection(f32 A1x, f32 A1z, f32 A2x, f32 A2z, f32 B1x, f32 B1z, f32 B2x, f32 B2z, f32 *interX, f32 *interZ, f32 *squared_dist)
Definition cam_math.c:123
Collider * colliderList
s32 b32
union CameraControlSettings::@14 points
#define transform_point
#define sqrtf
#define sin_deg
#define guTranslateF
#define guMtxCatF
#define guPerspectiveF
#define cos_deg
@ CAM_CONTROL_FIXED_POS_AND_ORIENTATION
Definition enums.h:4809
@ CAM_CONTROL_CONSTRAIN_TO_LINE
Definition enums.h:4803
@ CAM_CONTROL_CONSTAIN_BETWEEN_POINTS
Definition enums.h:4817
@ CAM_CONTROL_LOOK_AT_POINT_CONSTAIN_TO_LINE
Definition enums.h:4813
@ CAMERA_FLAG_SUPRESS_LEADING
Definition enums.h:4727
@ PA_FLAG_RIDING_PARTNER
Definition enums.h:3116
@ CAM_DEFAULT
Definition enums.h:1800
s32 test_ray_zones(f32 startX, f32 startY, f32 startZ, f32 dirX, f32 dirY, f32 dirZ, f32 *hitX, f32 *hitY, f32 *hitZ, f32 *hitDepth, f32 *nx, f32 *ny, f32 *nz)
Definition collision.c:866
f32 fabsf(f32 f)
f32 cos_rad(f32 x)
Definition 43F0.c:715
f32 sin_rad(f32 x)
Definition 43F0.c:711
#define PI
Definition macros.h:130
#define DEG_TO_RAD(deg)
Definition macros.h:138
#define SQ(x)
Definition macros.h:170
PlayerStatus * gPlayerStatusPtr
CollisionData gZoneCollisionData
Definition collision.c:36
PartnerStatus gPartnerStatus
Definition partners.c:42