rendering_graph_node.c (42688B)
1 #include <PR/ultratypes.h> 2 3 #include "area.h" 4 #include "engine/math_util.h" 5 #include "game_init.h" 6 #include "gfx_dimensions.h" 7 #include "main.h" 8 #include "memory.h" 9 #include "print.h" 10 #include "rendering_graph_node.h" 11 #include "shadow.h" 12 #include "sm64.h" 13 14 /** 15 * This file contains the code that processes the scene graph for rendering. 16 * The scene graph is responsible for drawing everything except the HUD / text boxes. 17 * First the root of the scene graph is processed when geo_process_root 18 * is called from level_script.c. The rest of the tree is traversed recursively 19 * using the function geo_process_node_and_siblings, which switches over all 20 * geo node types and calls a specialized function accordingly. 21 * The types are defined in engine/graph_node.h 22 * 23 * The scene graph typically looks like: 24 * - Root (viewport) 25 * - Master list 26 * - Ortho projection 27 * - Background (skybox) 28 * - Master list 29 * - Perspective 30 * - Camera 31 * - <area-specific display lists> 32 * - Object parent 33 * - <group with 240 object nodes> 34 * - Master list 35 * - Script node (Cannon overlay) 36 * 37 */ 38 39 s16 gMatStackIndex; 40 Mat4 gMatStack[32]; 41 Mtx *gMatStackFixed[32]; 42 43 /** 44 * Animation nodes have state in global variables, so this struct captures 45 * the animation state so a 'context switch' can be made when rendering the 46 * held object. 47 */ 48 struct GeoAnimState { 49 /*0x00*/ u8 type; 50 /*0x01*/ u8 enabled; 51 /*0x02*/ s16 frame; 52 /*0x04*/ f32 translationMultiplier; 53 /*0x08*/ u16 *attribute; 54 /*0x0C*/ s16 *data; 55 }; 56 57 // For some reason, this is a GeoAnimState struct, but the current state consists 58 // of separate global variables. It won't match EU otherwise. 59 struct GeoAnimState gGeoTempState; 60 61 u8 gCurrAnimType; 62 u8 gCurrAnimEnabled; 63 s16 gCurrAnimFrame; 64 f32 gCurrAnimTranslationMultiplier; 65 u16 *gCurrAnimAttribute; 66 s16 *gCurrAnimData; 67 68 struct AllocOnlyPool *gDisplayListHeap; 69 70 struct RenderModeContainer { 71 u32 modes[8]; 72 }; 73 74 /* Rendermode settings for cycle 1 for all 8 layers. */ 75 struct RenderModeContainer renderModeTable_1Cycle[2] = { { { 76 G_RM_OPA_SURF, 77 G_RM_AA_OPA_SURF, 78 G_RM_AA_OPA_SURF, 79 G_RM_AA_OPA_SURF, 80 G_RM_AA_TEX_EDGE, 81 G_RM_AA_XLU_SURF, 82 G_RM_AA_XLU_SURF, 83 G_RM_AA_XLU_SURF, 84 } }, 85 { { 86 /* z-buffered */ 87 G_RM_ZB_OPA_SURF, 88 G_RM_AA_ZB_OPA_SURF, 89 G_RM_AA_ZB_OPA_DECAL, 90 G_RM_AA_ZB_OPA_INTER, 91 G_RM_AA_ZB_TEX_EDGE, 92 G_RM_AA_ZB_XLU_SURF, 93 G_RM_AA_ZB_XLU_DECAL, 94 G_RM_AA_ZB_XLU_INTER, 95 } } }; 96 97 /* Rendermode settings for cycle 2 for all 8 layers. */ 98 struct RenderModeContainer renderModeTable_2Cycle[2] = { { { 99 G_RM_OPA_SURF2, 100 G_RM_AA_OPA_SURF2, 101 G_RM_AA_OPA_SURF2, 102 G_RM_AA_OPA_SURF2, 103 G_RM_AA_TEX_EDGE2, 104 G_RM_AA_XLU_SURF2, 105 G_RM_AA_XLU_SURF2, 106 G_RM_AA_XLU_SURF2, 107 } }, 108 { { 109 /* z-buffered */ 110 G_RM_ZB_OPA_SURF2, 111 G_RM_AA_ZB_OPA_SURF2, 112 G_RM_AA_ZB_OPA_DECAL2, 113 G_RM_AA_ZB_OPA_INTER2, 114 G_RM_AA_ZB_TEX_EDGE2, 115 G_RM_AA_ZB_XLU_SURF2, 116 G_RM_AA_ZB_XLU_DECAL2, 117 G_RM_AA_ZB_XLU_INTER2, 118 } } }; 119 120 struct GraphNodeRoot *gCurGraphNodeRoot = NULL; 121 struct GraphNodeMasterList *gCurGraphNodeMasterList = NULL; 122 struct GraphNodePerspective *gCurGraphNodeCamFrustum = NULL; 123 struct GraphNodeCamera *gCurGraphNodeCamera = NULL; 124 struct GraphNodeObject *gCurGraphNodeObject = NULL; 125 struct GraphNodeHeldObject *gCurGraphNodeHeldObject = NULL; 126 u16 gAreaUpdateCounter = 0; 127 128 #ifdef F3DEX_GBI_2 129 LookAt lookAt; 130 #endif 131 132 /** 133 * Process a master list node. 134 */ 135 static void geo_process_master_list_sub(struct GraphNodeMasterList *node) { 136 struct DisplayListNode *currList; 137 s32 i; 138 s32 enableZBuffer = (node->node.flags & GRAPH_RENDER_Z_BUFFER) != 0; 139 struct RenderModeContainer *modeList = &renderModeTable_1Cycle[enableZBuffer]; 140 struct RenderModeContainer *mode2List = &renderModeTable_2Cycle[enableZBuffer]; 141 142 // @bug This is where the LookAt values should be calculated but aren't. 143 // As a result, environment mapping is broken on Fast3DEX2 without the 144 // changes below. 145 #ifdef F3DEX_GBI_2 146 Mtx lMtx; 147 guLookAtReflect(&lMtx, &lookAt, 0, 0, 0, /* eye */ 0, 0, 1, /* at */ 1, 0, 0 /* up */); 148 #endif 149 150 if (enableZBuffer != 0) { 151 gDPPipeSync(gDisplayListHead++); 152 gSPSetGeometryMode(gDisplayListHead++, G_ZBUFFER); 153 } 154 155 for (i = 0; i < GFX_NUM_MASTER_LISTS; i++) { 156 if ((currList = node->listHeads[i]) != NULL) { 157 gDPSetRenderMode(gDisplayListHead++, modeList->modes[i], mode2List->modes[i]); 158 while (currList != NULL) { 159 gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(currList->transform), 160 G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH); 161 gSPDisplayList(gDisplayListHead++, currList->displayList); 162 currList = currList->next; 163 } 164 } 165 } 166 if (enableZBuffer != 0) { 167 gDPPipeSync(gDisplayListHead++); 168 gSPClearGeometryMode(gDisplayListHead++, G_ZBUFFER); 169 } 170 } 171 172 /** 173 * Appends the display list to one of the master lists based on the layer 174 * parameter. Look at the RenderModeContainer struct to see the corresponding 175 * render modes of layers. 176 */ 177 static void geo_append_display_list(void *displayList, s16 layer) { 178 179 #ifdef F3DEX_GBI_2 180 gSPLookAt(gDisplayListHead++, &lookAt); 181 #endif 182 if (gCurGraphNodeMasterList != 0) { 183 struct DisplayListNode *listNode = 184 alloc_only_pool_alloc(gDisplayListHeap, sizeof(struct DisplayListNode)); 185 186 listNode->transform = gMatStackFixed[gMatStackIndex]; 187 listNode->displayList = displayList; 188 listNode->next = 0; 189 if (gCurGraphNodeMasterList->listHeads[layer] == 0) { 190 gCurGraphNodeMasterList->listHeads[layer] = listNode; 191 } else { 192 gCurGraphNodeMasterList->listTails[layer]->next = listNode; 193 } 194 gCurGraphNodeMasterList->listTails[layer] = listNode; 195 } 196 } 197 198 /** 199 * Process the master list node. 200 */ 201 static void geo_process_master_list(struct GraphNodeMasterList *node) { 202 s32 i; 203 UNUSED u8 filler[4]; 204 205 if (gCurGraphNodeMasterList == NULL && node->node.children != NULL) { 206 gCurGraphNodeMasterList = node; 207 for (i = 0; i < GFX_NUM_MASTER_LISTS; i++) { 208 node->listHeads[i] = NULL; 209 } 210 geo_process_node_and_siblings(node->node.children); 211 geo_process_master_list_sub(node); 212 gCurGraphNodeMasterList = NULL; 213 } 214 } 215 216 /** 217 * Process an orthographic projection node. 218 */ 219 static void geo_process_ortho_projection(struct GraphNodeOrthoProjection *node) { 220 if (node->node.children != NULL) { 221 Mtx *mtx = alloc_display_list(sizeof(*mtx)); 222 f32 left = (gCurGraphNodeRoot->x - gCurGraphNodeRoot->width) / 2.0f * node->scale; 223 f32 right = (gCurGraphNodeRoot->x + gCurGraphNodeRoot->width) / 2.0f * node->scale; 224 f32 top = (gCurGraphNodeRoot->y - gCurGraphNodeRoot->height) / 2.0f * node->scale; 225 f32 bottom = (gCurGraphNodeRoot->y + gCurGraphNodeRoot->height) / 2.0f * node->scale; 226 227 guOrtho(mtx, left, right, bottom, top, -2.0f, 2.0f, 1.0f); 228 gSPPerspNormalize(gDisplayListHead++, 0xFFFF); 229 gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtx), G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH); 230 231 geo_process_node_and_siblings(node->node.children); 232 } 233 } 234 235 /** 236 * Process a perspective projection node. 237 */ 238 static void geo_process_perspective(struct GraphNodePerspective *node) { 239 if (node->fnNode.func != NULL) { 240 node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, gMatStack[gMatStackIndex]); 241 } 242 if (node->fnNode.node.children != NULL) { 243 u16 perspNorm; 244 Mtx *mtx = alloc_display_list(sizeof(*mtx)); 245 246 #ifdef VERSION_EU 247 f32 aspect = ((f32) gCurGraphNodeRoot->width / (f32) gCurGraphNodeRoot->height) * 1.1f; 248 #else 249 f32 aspect = (f32) gCurGraphNodeRoot->width / (f32) gCurGraphNodeRoot->height; 250 #endif 251 252 guPerspective(mtx, &perspNorm, node->fov, aspect, node->near, node->far, 1.0f); 253 gSPPerspNormalize(gDisplayListHead++, perspNorm); 254 255 gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtx), G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH); 256 257 gCurGraphNodeCamFrustum = node; 258 geo_process_node_and_siblings(node->fnNode.node.children); 259 gCurGraphNodeCamFrustum = NULL; 260 } 261 } 262 263 /** 264 * Process a level of detail node. From the current transformation matrix, 265 * the perpendicular distance to the camera is extracted and the children 266 * of this node are only processed if that distance is within the render 267 * range of this node. 268 */ 269 static void geo_process_level_of_detail(struct GraphNodeLevelOfDetail *node) { 270 #ifdef GBI_FLOATS 271 Mtx *mtx = gMatStackFixed[gMatStackIndex]; 272 s16 distanceFromCam = (s32) -mtx->m[3][2]; // z-component of the translation column 273 #else 274 // The fixed point Mtx type is defined as 16 longs, but it's actually 16 275 // shorts for the integer parts followed by 16 shorts for the fraction parts 276 Mtx *mtx = gMatStackFixed[gMatStackIndex]; 277 s16 distanceFromCam = -GET_HIGH_S16_OF_32(mtx->m[1][3]); // z-component of the translation column 278 #endif 279 280 if (node->minDistance <= distanceFromCam && distanceFromCam < node->maxDistance) { 281 if (node->node.children != 0) { 282 geo_process_node_and_siblings(node->node.children); 283 } 284 } 285 } 286 287 /** 288 * Process a switch case node. The node's selection function is called 289 * if it is 0, and among the node's children, only the selected child is 290 * processed next. 291 */ 292 static void geo_process_switch(struct GraphNodeSwitchCase *node) { 293 struct GraphNode *selectedChild = node->fnNode.node.children; 294 s32 i; 295 296 if (node->fnNode.func != NULL) { 297 node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, gMatStack[gMatStackIndex]); 298 } 299 for (i = 0; selectedChild != NULL && node->selectedCase > i; i++) { 300 selectedChild = selectedChild->next; 301 } 302 if (selectedChild != NULL) { 303 geo_process_node_and_siblings(selectedChild); 304 } 305 } 306 307 /** 308 * Process a camera node. 309 */ 310 static void geo_process_camera(struct GraphNodeCamera *node) { 311 Mat4 cameraTransform; 312 Mtx *rollMtx = alloc_display_list(sizeof(*rollMtx)); 313 Mtx *mtx = alloc_display_list(sizeof(*mtx)); 314 315 if (node->fnNode.func != NULL) { 316 node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, gMatStack[gMatStackIndex]); 317 } 318 mtxf_rotate_xy(rollMtx, node->rollScreen); 319 320 gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(rollMtx), G_MTX_PROJECTION | G_MTX_MUL | G_MTX_NOPUSH); 321 322 mtxf_lookat(cameraTransform, node->pos, node->focus, node->roll); 323 mtxf_mul(gMatStack[gMatStackIndex + 1], cameraTransform, gMatStack[gMatStackIndex]); 324 gMatStackIndex++; 325 mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); 326 gMatStackFixed[gMatStackIndex] = mtx; 327 if (node->fnNode.node.children != 0) { 328 gCurGraphNodeCamera = node; 329 node->matrixPtr = &gMatStack[gMatStackIndex]; 330 geo_process_node_and_siblings(node->fnNode.node.children); 331 gCurGraphNodeCamera = NULL; 332 } 333 gMatStackIndex--; 334 } 335 336 /** 337 * Process a translation / rotation node. A transformation matrix based 338 * on the node's translation and rotation is created and pushed on both 339 * the float and fixed point matrix stacks. 340 * For the rest it acts as a normal display list node. 341 */ 342 static void geo_process_translation_rotation(struct GraphNodeTranslationRotation *node) { 343 Mat4 mtxf; 344 Vec3f translation; 345 Mtx *mtx = alloc_display_list(sizeof(*mtx)); 346 347 vec3s_to_vec3f(translation, node->translation); 348 mtxf_rotate_zxy_and_translate(mtxf, translation, node->rotation); 349 mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]); 350 gMatStackIndex++; 351 mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); 352 gMatStackFixed[gMatStackIndex] = mtx; 353 if (node->displayList != NULL) { 354 geo_append_display_list(node->displayList, node->node.flags >> 8); 355 } 356 if (node->node.children != NULL) { 357 geo_process_node_and_siblings(node->node.children); 358 } 359 gMatStackIndex--; 360 } 361 362 /** 363 * Process a translation node. A transformation matrix based on the node's 364 * translation is created and pushed on both the float and fixed point matrix stacks. 365 * For the rest it acts as a normal display list node. 366 */ 367 static void geo_process_translation(struct GraphNodeTranslation *node) { 368 Mat4 mtxf; 369 Vec3f translation; 370 Mtx *mtx = alloc_display_list(sizeof(*mtx)); 371 372 vec3s_to_vec3f(translation, node->translation); 373 mtxf_rotate_zxy_and_translate(mtxf, translation, gVec3sZero); 374 mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]); 375 gMatStackIndex++; 376 mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); 377 gMatStackFixed[gMatStackIndex] = mtx; 378 if (node->displayList != NULL) { 379 geo_append_display_list(node->displayList, node->node.flags >> 8); 380 } 381 if (node->node.children != NULL) { 382 geo_process_node_and_siblings(node->node.children); 383 } 384 gMatStackIndex--; 385 } 386 387 /** 388 * Process a rotation node. A transformation matrix based on the node's 389 * rotation is created and pushed on both the float and fixed point matrix stacks. 390 * For the rest it acts as a normal display list node. 391 */ 392 static void geo_process_rotation(struct GraphNodeRotation *node) { 393 Mat4 mtxf; 394 Mtx *mtx = alloc_display_list(sizeof(*mtx)); 395 396 mtxf_rotate_zxy_and_translate(mtxf, gVec3fZero, node->rotation); 397 mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]); 398 gMatStackIndex++; 399 mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); 400 gMatStackFixed[gMatStackIndex] = mtx; 401 if (node->displayList != NULL) { 402 geo_append_display_list(node->displayList, node->node.flags >> 8); 403 } 404 if (node->node.children != NULL) { 405 geo_process_node_and_siblings(node->node.children); 406 } 407 gMatStackIndex--; 408 } 409 410 /** 411 * Process a scaling node. A transformation matrix based on the node's 412 * scale is created and pushed on both the float and fixed point matrix stacks. 413 * For the rest it acts as a normal display list node. 414 */ 415 static void geo_process_scale(struct GraphNodeScale *node) { 416 UNUSED Mat4 transform; 417 Vec3f scaleVec; 418 Mtx *mtx = alloc_display_list(sizeof(*mtx)); 419 420 vec3f_set(scaleVec, node->scale, node->scale, node->scale); 421 mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex], scaleVec); 422 gMatStackIndex++; 423 mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); 424 gMatStackFixed[gMatStackIndex] = mtx; 425 if (node->displayList != NULL) { 426 geo_append_display_list(node->displayList, node->node.flags >> 8); 427 } 428 if (node->node.children != NULL) { 429 geo_process_node_and_siblings(node->node.children); 430 } 431 gMatStackIndex--; 432 } 433 434 /** 435 * Process a billboard node. A transformation matrix is created that makes its 436 * children face the camera, and it is pushed on the floating point and fixed 437 * point matrix stacks. 438 * For the rest it acts as a normal display list node. 439 */ 440 static void geo_process_billboard(struct GraphNodeBillboard *node) { 441 Vec3f translation; 442 Mtx *mtx = alloc_display_list(sizeof(*mtx)); 443 444 gMatStackIndex++; 445 vec3s_to_vec3f(translation, node->translation); 446 mtxf_billboard(gMatStack[gMatStackIndex], gMatStack[gMatStackIndex - 1], translation, 447 gCurGraphNodeCamera->roll); 448 if (gCurGraphNodeHeldObject != NULL) { 449 mtxf_scale_vec3f(gMatStack[gMatStackIndex], gMatStack[gMatStackIndex], 450 gCurGraphNodeHeldObject->objNode->header.gfx.scale); 451 } else if (gCurGraphNodeObject != NULL) { 452 mtxf_scale_vec3f(gMatStack[gMatStackIndex], gMatStack[gMatStackIndex], 453 gCurGraphNodeObject->scale); 454 } 455 456 mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); 457 gMatStackFixed[gMatStackIndex] = mtx; 458 459 if (node->displayList != NULL) { 460 geo_append_display_list(node->displayList, node->node.flags >> 8); 461 } 462 463 if (node->node.children != NULL) { 464 geo_process_node_and_siblings(node->node.children); 465 } 466 467 gMatStackIndex--; 468 } 469 470 /** 471 * Process a display list node. It draws a display list without first pushing 472 * a transformation on the stack, so all transformations are inherited from the 473 * parent node. It processes its children if it has them. 474 */ 475 static void geo_process_display_list(struct GraphNodeDisplayList *node) { 476 if (node->displayList != NULL) { 477 geo_append_display_list(node->displayList, node->node.flags >> 8); 478 } 479 if (node->node.children != NULL) { 480 geo_process_node_and_siblings(node->node.children); 481 } 482 } 483 484 /** 485 * Process a generated list. Instead of storing a pointer to a display list, 486 * the list is generated on the fly by a function. 487 */ 488 static void geo_process_generated_list(struct GraphNodeGenerated *node) { 489 if (node->fnNode.func != NULL) { 490 Gfx *list = node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, 491 (struct AllocOnlyPool *) gMatStack[gMatStackIndex]); 492 493 if (list != NULL) { 494 geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(list), node->fnNode.node.flags >> 8); 495 } 496 } 497 if (node->fnNode.node.children != NULL) { 498 geo_process_node_and_siblings(node->fnNode.node.children); 499 } 500 } 501 502 /** 503 * Process a background node. Tries to retrieve a background display list from 504 * the function of the node. If that function is null or returns null, a black 505 * rectangle is drawn instead. 506 */ 507 static void geo_process_background(struct GraphNodeBackground *node) { 508 Gfx *list = NULL; 509 510 if (node->fnNode.func != NULL) { 511 list = node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, 512 (struct AllocOnlyPool *) gMatStack[gMatStackIndex]); 513 } 514 if (list != NULL) { 515 geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(list), node->fnNode.node.flags >> 8); 516 } else if (gCurGraphNodeMasterList != NULL) { 517 #ifndef F3DEX_GBI_2E 518 Gfx *gfxStart = alloc_display_list(sizeof(Gfx) * 7); 519 #else 520 Gfx *gfxStart = alloc_display_list(sizeof(Gfx) * 8); 521 #endif 522 Gfx *gfx = gfxStart; 523 524 gDPPipeSync(gfx++); 525 gDPSetCycleType(gfx++, G_CYC_FILL); 526 gDPSetFillColor(gfx++, node->background); 527 gDPFillRectangle(gfx++, GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(0), BORDER_HEIGHT, 528 GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(0) - 1, SCREEN_HEIGHT - BORDER_HEIGHT - 1); 529 gDPPipeSync(gfx++); 530 gDPSetCycleType(gfx++, G_CYC_1CYCLE); 531 gSPEndDisplayList(gfx++); 532 533 geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(gfxStart), 0); 534 } 535 if (node->fnNode.node.children != NULL) { 536 geo_process_node_and_siblings(node->fnNode.node.children); 537 } 538 } 539 540 /** 541 * Render an animated part. The current animation state is not part of the node 542 * but set in global variables. If an animated part is skipped, everything afterwards desyncs. 543 */ 544 static void geo_process_animated_part(struct GraphNodeAnimatedPart *node) { 545 Mat4 matrix; 546 Vec3s rotation; 547 Vec3f translation; 548 Mtx *matrixPtr = alloc_display_list(sizeof(*matrixPtr)); 549 550 vec3s_copy(rotation, gVec3sZero); 551 vec3f_set(translation, node->translation[0], node->translation[1], node->translation[2]); 552 if (gCurrAnimType == ANIM_TYPE_TRANSLATION) { 553 translation[0] += gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] 554 * gCurrAnimTranslationMultiplier; 555 translation[1] += gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] 556 * gCurrAnimTranslationMultiplier; 557 translation[2] += gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] 558 * gCurrAnimTranslationMultiplier; 559 gCurrAnimType = ANIM_TYPE_ROTATION; 560 } else { 561 if (gCurrAnimType == ANIM_TYPE_LATERAL_TRANSLATION) { 562 translation[0] += 563 gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] 564 * gCurrAnimTranslationMultiplier; 565 gCurrAnimAttribute += 2; 566 translation[2] += 567 gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] 568 * gCurrAnimTranslationMultiplier; 569 gCurrAnimType = ANIM_TYPE_ROTATION; 570 } else { 571 if (gCurrAnimType == ANIM_TYPE_VERTICAL_TRANSLATION) { 572 gCurrAnimAttribute += 2; 573 translation[1] += 574 gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] 575 * gCurrAnimTranslationMultiplier; 576 gCurrAnimAttribute += 2; 577 gCurrAnimType = ANIM_TYPE_ROTATION; 578 } else if (gCurrAnimType == ANIM_TYPE_NO_TRANSLATION) { 579 gCurrAnimAttribute += 6; 580 gCurrAnimType = ANIM_TYPE_ROTATION; 581 } 582 } 583 } 584 585 if (gCurrAnimType == ANIM_TYPE_ROTATION) { 586 rotation[0] = gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]; 587 rotation[1] = gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]; 588 rotation[2] = gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]; 589 } 590 mtxf_rotate_xyz_and_translate(matrix, translation, rotation); 591 mtxf_mul(gMatStack[gMatStackIndex + 1], matrix, gMatStack[gMatStackIndex]); 592 gMatStackIndex++; 593 mtxf_to_mtx(matrixPtr, gMatStack[gMatStackIndex]); 594 gMatStackFixed[gMatStackIndex] = matrixPtr; 595 if (node->displayList != NULL) { 596 geo_append_display_list(node->displayList, node->node.flags >> 8); 597 } 598 if (node->node.children != NULL) { 599 geo_process_node_and_siblings(node->node.children); 600 } 601 gMatStackIndex--; 602 } 603 604 /** 605 * Initialize the animation-related global variables for the currently drawn 606 * object's animation. 607 */ 608 void geo_set_animation_globals(struct AnimInfo *node, s32 hasAnimation) { 609 struct Animation *anim = node->curAnim; 610 611 if (hasAnimation) { 612 node->animFrame = geo_update_animation_frame(node, &node->animFrameAccelAssist); 613 } 614 node->animTimer = gAreaUpdateCounter; 615 if (anim->flags & ANIM_FLAG_HOR_TRANS) { 616 gCurrAnimType = ANIM_TYPE_VERTICAL_TRANSLATION; 617 } else if (anim->flags & ANIM_FLAG_VERT_TRANS) { 618 gCurrAnimType = ANIM_TYPE_LATERAL_TRANSLATION; 619 } else if (anim->flags & ANIM_FLAG_6) { 620 gCurrAnimType = ANIM_TYPE_NO_TRANSLATION; 621 } else { 622 gCurrAnimType = ANIM_TYPE_TRANSLATION; 623 } 624 625 gCurrAnimFrame = node->animFrame; 626 gCurrAnimEnabled = (anim->flags & ANIM_FLAG_5) == 0; 627 gCurrAnimAttribute = segmented_to_virtual((void *) anim->index); 628 gCurrAnimData = segmented_to_virtual((void *) anim->values); 629 630 if (anim->animYTransDivisor == 0) { 631 gCurrAnimTranslationMultiplier = 1.0f; 632 } else { 633 gCurrAnimTranslationMultiplier = (f32) node->animYTrans / (f32) anim->animYTransDivisor; 634 } 635 } 636 637 /** 638 * Process a shadow node. Renders a shadow under an object offset by the 639 * translation of the first animated component and rotated according to 640 * the floor below it. 641 */ 642 static void geo_process_shadow(struct GraphNodeShadow *node) { 643 Gfx *shadowList; 644 Mat4 mtxf; 645 Vec3f shadowPos; 646 Vec3f animOffset; 647 f32 objScale; 648 f32 shadowScale; 649 f32 sinAng; 650 f32 cosAng; 651 struct GraphNode *geo; 652 Mtx *mtx; 653 654 if (gCurGraphNodeCamera != NULL && gCurGraphNodeObject != NULL) { 655 if (gCurGraphNodeHeldObject != NULL) { 656 get_pos_from_transform_mtx(shadowPos, gMatStack[gMatStackIndex], 657 *gCurGraphNodeCamera->matrixPtr); 658 shadowScale = node->shadowScale; 659 } else { 660 vec3f_copy(shadowPos, gCurGraphNodeObject->pos); 661 shadowScale = node->shadowScale * gCurGraphNodeObject->scale[0]; 662 } 663 664 objScale = 1.0f; 665 if (gCurrAnimEnabled) { 666 if (gCurrAnimType == ANIM_TYPE_TRANSLATION 667 || gCurrAnimType == ANIM_TYPE_LATERAL_TRANSLATION) { 668 geo = node->node.children; 669 if (geo != NULL && geo->type == GRAPH_NODE_TYPE_SCALE) { 670 objScale = ((struct GraphNodeScale *) geo)->scale; 671 } 672 animOffset[0] = 673 gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] 674 * gCurrAnimTranslationMultiplier * objScale; 675 animOffset[1] = 0.0f; 676 gCurrAnimAttribute += 2; 677 animOffset[2] = 678 gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] 679 * gCurrAnimTranslationMultiplier * objScale; 680 gCurrAnimAttribute -= 6; 681 682 // simple matrix rotation so the shadow offset rotates along with the object 683 sinAng = sins(gCurGraphNodeObject->angle[1]); 684 cosAng = coss(gCurGraphNodeObject->angle[1]); 685 686 shadowPos[0] += animOffset[0] * cosAng + animOffset[2] * sinAng; 687 shadowPos[2] += -animOffset[0] * sinAng + animOffset[2] * cosAng; 688 } 689 } 690 691 shadowList = create_shadow_below_xyz(shadowPos[0], shadowPos[1], shadowPos[2], shadowScale, 692 node->shadowSolidity, node->shadowType); 693 if (shadowList != NULL) { 694 mtx = alloc_display_list(sizeof(*mtx)); 695 gMatStackIndex++; 696 mtxf_translate(mtxf, shadowPos); 697 mtxf_mul(gMatStack[gMatStackIndex], mtxf, *gCurGraphNodeCamera->matrixPtr); 698 mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); 699 gMatStackFixed[gMatStackIndex] = mtx; 700 if (gShadowAboveWaterOrLava == TRUE) { 701 geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(shadowList), 4); 702 } else if (gMarioOnIceOrCarpet == 1) { 703 geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(shadowList), 5); 704 } else { 705 geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(shadowList), 6); 706 } 707 gMatStackIndex--; 708 } 709 } 710 711 if (node->node.children != NULL) { 712 geo_process_node_and_siblings(node->node.children); 713 } 714 } 715 716 /** 717 * Check whether an object is in view to determine whether it should be drawn. 718 * This is known as frustum culling. 719 * It checks whether the object is far away, very close / behind the camera, 720 * or horizontally out of view. It does not check whether it is vertically 721 * out of view. It assumes a sphere of 300 units around the object's position 722 * unless the object has a culling radius node that specifies otherwise. 723 * 724 * The matrix parameter should be the top of the matrix stack, which is the 725 * object's transformation matrix times the camera 'look-at' matrix. The math 726 * is counter-intuitive, but it checks column 3 (translation vector) of this 727 * matrix to determine where the origin (0,0,0) in object space will be once 728 * transformed to camera space (x+ = right, y+ = up, z = 'coming out the screen'). 729 * In 3D graphics, you typically model the world as being moved in front of a 730 * static camera instead of a moving camera through a static world, which in 731 * this case simplifies calculations. Note that the perspective matrix is not 732 * on the matrix stack, so there are still calculations with the fov to compute 733 * the slope of the lines of the frustum. 734 * 735 * z- 736 * 737 * \ | / 738 * \ | / 739 * \ | / 740 * \ | / 741 * \ | / 742 * \|/ 743 * C x+ 744 * 745 * Since (0,0,0) is unaffected by rotation, columns 0, 1 and 2 are ignored. 746 */ 747 static s32 obj_is_in_view(struct GraphNodeObject *node, Mat4 matrix) { 748 s16 cullingRadius; 749 s16 halfFov; // half of the fov in in-game angle units instead of degrees 750 struct GraphNode *geo; 751 f32 hScreenEdge; 752 753 if (node->node.flags & GRAPH_RENDER_INVISIBLE) { 754 return FALSE; 755 } 756 757 geo = node->sharedChild; 758 759 // ! @bug The aspect ratio is not accounted for. When the fov value is 45, 760 // the horizontal effective fov is actually 60 degrees, so you can see objects 761 // visibly pop in or out at the edge of the screen. 762 halfFov = (gCurGraphNodeCamFrustum->fov / 2.0f + 1.0f) * 32768.0f / 180.0f + 0.5f; 763 764 hScreenEdge = -matrix[3][2] * sins(halfFov) / coss(halfFov); 765 // -matrix[3][2] is the depth, which gets multiplied by tan(halfFov) to get 766 // the amount of units between the center of the screen and the horizontal edge 767 // given the distance from the object to the camera. 768 769 #ifdef WIDESCREEN 770 // This multiplication should really be performed on 4:3 as well, 771 // but the issue will be more apparent on widescreen. 772 hScreenEdge *= GFX_DIMENSIONS_ASPECT_RATIO; 773 #endif 774 775 if (geo != NULL && geo->type == GRAPH_NODE_TYPE_CULLING_RADIUS) { 776 cullingRadius = 777 (f32)((struct GraphNodeCullingRadius *) geo)->cullingRadius; //! Why is there a f32 cast? 778 } else { 779 cullingRadius = 300; 780 } 781 782 // Don't render if the object is close to or behind the camera 783 if (matrix[3][2] > -100.0f + cullingRadius) { 784 return FALSE; 785 } 786 787 //! This makes the HOLP not update when the camera is far away, and it 788 // makes PU travel safe when the camera is locked on the main map. 789 // If Mario were rendered with a depth over 65536 it would cause overflow 790 // when converting the transformation matrix to a fixed point matrix. 791 if (matrix[3][2] < -20000.0f - cullingRadius) { 792 return FALSE; 793 } 794 795 // Check whether the object is horizontally in view 796 if (matrix[3][0] > hScreenEdge + cullingRadius) { 797 return FALSE; 798 } 799 if (matrix[3][0] < -hScreenEdge - cullingRadius) { 800 return FALSE; 801 } 802 return TRUE; 803 } 804 805 /** 806 * Process an object node. 807 */ 808 static void geo_process_object(struct Object *node) { 809 Mat4 mtxf; 810 s32 hasAnimation = (node->header.gfx.node.flags & GRAPH_RENDER_HAS_ANIMATION) != 0; 811 812 if (node->header.gfx.areaIndex == gCurGraphNodeRoot->areaIndex) { 813 if (node->header.gfx.throwMatrix != NULL) { 814 mtxf_mul(gMatStack[gMatStackIndex + 1], *node->header.gfx.throwMatrix, 815 gMatStack[gMatStackIndex]); 816 } else if (node->header.gfx.node.flags & GRAPH_RENDER_BILLBOARD) { 817 mtxf_billboard(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex], 818 node->header.gfx.pos, gCurGraphNodeCamera->roll); 819 } else { 820 mtxf_rotate_zxy_and_translate(mtxf, node->header.gfx.pos, node->header.gfx.angle); 821 mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]); 822 } 823 824 mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex + 1], 825 node->header.gfx.scale); 826 node->header.gfx.throwMatrix = &gMatStack[++gMatStackIndex]; 827 node->header.gfx.cameraToObject[0] = gMatStack[gMatStackIndex][3][0]; 828 node->header.gfx.cameraToObject[1] = gMatStack[gMatStackIndex][3][1]; 829 node->header.gfx.cameraToObject[2] = gMatStack[gMatStackIndex][3][2]; 830 831 // FIXME: correct types 832 if (node->header.gfx.animInfo.curAnim != NULL) { 833 geo_set_animation_globals(&node->header.gfx.animInfo, hasAnimation); 834 } 835 if (obj_is_in_view(&node->header.gfx, gMatStack[gMatStackIndex])) { 836 Mtx *mtx = alloc_display_list(sizeof(*mtx)); 837 838 mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); 839 gMatStackFixed[gMatStackIndex] = mtx; 840 if (node->header.gfx.sharedChild != NULL) { 841 gCurGraphNodeObject = (struct GraphNodeObject *) node; 842 node->header.gfx.sharedChild->parent = &node->header.gfx.node; 843 geo_process_node_and_siblings(node->header.gfx.sharedChild); 844 node->header.gfx.sharedChild->parent = NULL; 845 gCurGraphNodeObject = NULL; 846 } 847 if (node->header.gfx.node.children != NULL) { 848 geo_process_node_and_siblings(node->header.gfx.node.children); 849 } 850 } 851 852 gMatStackIndex--; 853 gCurrAnimType = ANIM_TYPE_NONE; 854 node->header.gfx.throwMatrix = NULL; 855 } 856 } 857 858 /** 859 * Process an object parent node. Temporarily assigns itself as the parent of 860 * the subtree rooted at 'sharedChild' and processes the subtree, after which the 861 * actual children are be processed. (in practice they are null though) 862 */ 863 static void geo_process_object_parent(struct GraphNodeObjectParent *node) { 864 if (node->sharedChild != NULL) { 865 node->sharedChild->parent = (struct GraphNode *) node; 866 geo_process_node_and_siblings(node->sharedChild); 867 node->sharedChild->parent = NULL; 868 } 869 if (node->node.children != NULL) { 870 geo_process_node_and_siblings(node->node.children); 871 } 872 } 873 874 /** 875 * Process a held object node. 876 */ 877 void geo_process_held_object(struct GraphNodeHeldObject *node) { 878 Mat4 mat; 879 Vec3f translation; 880 Mtx *mtx = alloc_display_list(sizeof(*mtx)); 881 882 #ifdef F3DEX_GBI_2 883 gSPLookAt(gDisplayListHead++, &lookAt); 884 #endif 885 886 if (node->fnNode.func != NULL) { 887 node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, gMatStack[gMatStackIndex]); 888 } 889 if (node->objNode != NULL && node->objNode->header.gfx.sharedChild != NULL) { 890 s32 hasAnimation = (node->objNode->header.gfx.node.flags & GRAPH_RENDER_HAS_ANIMATION) != 0; 891 892 translation[0] = node->translation[0] / 4.0f; 893 translation[1] = node->translation[1] / 4.0f; 894 translation[2] = node->translation[2] / 4.0f; 895 896 mtxf_translate(mat, translation); 897 mtxf_copy(gMatStack[gMatStackIndex + 1], *gCurGraphNodeObject->throwMatrix); 898 gMatStack[gMatStackIndex + 1][3][0] = gMatStack[gMatStackIndex][3][0]; 899 gMatStack[gMatStackIndex + 1][3][1] = gMatStack[gMatStackIndex][3][1]; 900 gMatStack[gMatStackIndex + 1][3][2] = gMatStack[gMatStackIndex][3][2]; 901 mtxf_mul(gMatStack[gMatStackIndex + 1], mat, gMatStack[gMatStackIndex + 1]); 902 mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex + 1], 903 node->objNode->header.gfx.scale); 904 if (node->fnNode.func != NULL) { 905 node->fnNode.func(GEO_CONTEXT_HELD_OBJ, &node->fnNode.node, 906 (struct AllocOnlyPool *) gMatStack[gMatStackIndex + 1]); 907 } 908 gMatStackIndex++; 909 mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); 910 gMatStackFixed[gMatStackIndex] = mtx; 911 gGeoTempState.type = gCurrAnimType; 912 gGeoTempState.enabled = gCurrAnimEnabled; 913 gGeoTempState.frame = gCurrAnimFrame; 914 gGeoTempState.translationMultiplier = gCurrAnimTranslationMultiplier; 915 gGeoTempState.attribute = gCurrAnimAttribute; 916 gGeoTempState.data = gCurrAnimData; 917 gCurrAnimType = 0; 918 gCurGraphNodeHeldObject = (void *) node; 919 if (node->objNode->header.gfx.animInfo.curAnim != NULL) { 920 geo_set_animation_globals(&node->objNode->header.gfx.animInfo, hasAnimation); 921 } 922 923 geo_process_node_and_siblings(node->objNode->header.gfx.sharedChild); 924 gCurGraphNodeHeldObject = NULL; 925 gCurrAnimType = gGeoTempState.type; 926 gCurrAnimEnabled = gGeoTempState.enabled; 927 gCurrAnimFrame = gGeoTempState.frame; 928 gCurrAnimTranslationMultiplier = gGeoTempState.translationMultiplier; 929 gCurrAnimAttribute = gGeoTempState.attribute; 930 gCurrAnimData = gGeoTempState.data; 931 gMatStackIndex--; 932 } 933 934 if (node->fnNode.node.children != NULL) { 935 geo_process_node_and_siblings(node->fnNode.node.children); 936 } 937 } 938 939 /** 940 * Processes the children of the given GraphNode if it has any 941 */ 942 void geo_try_process_children(struct GraphNode *node) { 943 if (node->children != NULL) { 944 geo_process_node_and_siblings(node->children); 945 } 946 } 947 948 /** 949 * Process a generic geo node and its siblings. 950 * The first argument is the start node, and all its siblings will 951 * be iterated over. 952 */ 953 void geo_process_node_and_siblings(struct GraphNode *firstNode) { 954 s16 iterateChildren = TRUE; 955 struct GraphNode *curGraphNode = firstNode; 956 struct GraphNode *parent = curGraphNode->parent; 957 958 // In the case of a switch node, exactly one of the children of the node is 959 // processed instead of all children like usual 960 if (parent != NULL) { 961 iterateChildren = (parent->type != GRAPH_NODE_TYPE_SWITCH_CASE); 962 } 963 964 do { 965 if (curGraphNode->flags & GRAPH_RENDER_ACTIVE) { 966 if (curGraphNode->flags & GRAPH_RENDER_CHILDREN_FIRST) { 967 geo_try_process_children(curGraphNode); 968 } else { 969 switch (curGraphNode->type) { 970 case GRAPH_NODE_TYPE_ORTHO_PROJECTION: 971 geo_process_ortho_projection((struct GraphNodeOrthoProjection *) curGraphNode); 972 break; 973 case GRAPH_NODE_TYPE_PERSPECTIVE: 974 geo_process_perspective((struct GraphNodePerspective *) curGraphNode); 975 break; 976 case GRAPH_NODE_TYPE_MASTER_LIST: 977 geo_process_master_list((struct GraphNodeMasterList *) curGraphNode); 978 break; 979 case GRAPH_NODE_TYPE_LEVEL_OF_DETAIL: 980 geo_process_level_of_detail((struct GraphNodeLevelOfDetail *) curGraphNode); 981 break; 982 case GRAPH_NODE_TYPE_SWITCH_CASE: 983 geo_process_switch((struct GraphNodeSwitchCase *) curGraphNode); 984 break; 985 case GRAPH_NODE_TYPE_CAMERA: 986 geo_process_camera((struct GraphNodeCamera *) curGraphNode); 987 break; 988 case GRAPH_NODE_TYPE_TRANSLATION_ROTATION: 989 geo_process_translation_rotation( 990 (struct GraphNodeTranslationRotation *) curGraphNode); 991 break; 992 case GRAPH_NODE_TYPE_TRANSLATION: 993 geo_process_translation((struct GraphNodeTranslation *) curGraphNode); 994 break; 995 case GRAPH_NODE_TYPE_ROTATION: 996 geo_process_rotation((struct GraphNodeRotation *) curGraphNode); 997 break; 998 case GRAPH_NODE_TYPE_OBJECT: 999 geo_process_object((struct Object *) curGraphNode); 1000 break; 1001 case GRAPH_NODE_TYPE_ANIMATED_PART: 1002 geo_process_animated_part((struct GraphNodeAnimatedPart *) curGraphNode); 1003 break; 1004 case GRAPH_NODE_TYPE_BILLBOARD: 1005 geo_process_billboard((struct GraphNodeBillboard *) curGraphNode); 1006 break; 1007 case GRAPH_NODE_TYPE_DISPLAY_LIST: 1008 geo_process_display_list((struct GraphNodeDisplayList *) curGraphNode); 1009 break; 1010 case GRAPH_NODE_TYPE_SCALE: 1011 geo_process_scale((struct GraphNodeScale *) curGraphNode); 1012 break; 1013 case GRAPH_NODE_TYPE_SHADOW: 1014 geo_process_shadow((struct GraphNodeShadow *) curGraphNode); 1015 break; 1016 case GRAPH_NODE_TYPE_OBJECT_PARENT: 1017 geo_process_object_parent((struct GraphNodeObjectParent *) curGraphNode); 1018 break; 1019 case GRAPH_NODE_TYPE_GENERATED_LIST: 1020 geo_process_generated_list((struct GraphNodeGenerated *) curGraphNode); 1021 break; 1022 case GRAPH_NODE_TYPE_BACKGROUND: 1023 geo_process_background((struct GraphNodeBackground *) curGraphNode); 1024 break; 1025 case GRAPH_NODE_TYPE_HELD_OBJ: 1026 geo_process_held_object((struct GraphNodeHeldObject *) curGraphNode); 1027 break; 1028 default: 1029 geo_try_process_children((struct GraphNode *) curGraphNode); 1030 break; 1031 } 1032 } 1033 } else { 1034 if (curGraphNode->type == GRAPH_NODE_TYPE_OBJECT) { 1035 ((struct GraphNodeObject *) curGraphNode)->throwMatrix = NULL; 1036 } 1037 } 1038 } while (iterateChildren && (curGraphNode = curGraphNode->next) != firstNode); 1039 } 1040 1041 /** 1042 * Process a root node. This is the entry point for processing the scene graph. 1043 * The root node itself sets up the viewport, then all its children are processed 1044 * to set up the projection and draw display lists. 1045 */ 1046 void geo_process_root(struct GraphNodeRoot *node, Vp *b, Vp *c, s32 clearColor) { 1047 UNUSED u8 filler[4]; 1048 1049 if (node->node.flags & GRAPH_RENDER_ACTIVE) { 1050 Mtx *initialMatrix; 1051 Vp *viewport = alloc_display_list(sizeof(*viewport)); 1052 1053 gDisplayListHeap = alloc_only_pool_init(main_pool_available() - sizeof(struct AllocOnlyPool), 1054 MEMORY_POOL_LEFT); 1055 initialMatrix = alloc_display_list(sizeof(*initialMatrix)); 1056 gMatStackIndex = 0; 1057 gCurrAnimType = 0; 1058 vec3s_set(viewport->vp.vtrans, node->x * 4, node->y * 4, 511); 1059 vec3s_set(viewport->vp.vscale, node->width * 4, node->height * 4, 511); 1060 if (b != NULL) { 1061 clear_framebuffer(clearColor); 1062 make_viewport_clip_rect(b); 1063 *viewport = *b; 1064 } 1065 1066 else if (c != NULL) { 1067 clear_framebuffer(clearColor); 1068 make_viewport_clip_rect(c); 1069 } 1070 1071 mtxf_identity(gMatStack[gMatStackIndex]); 1072 mtxf_to_mtx(initialMatrix, gMatStack[gMatStackIndex]); 1073 gMatStackFixed[gMatStackIndex] = initialMatrix; 1074 gSPViewport(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(viewport)); 1075 gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(gMatStackFixed[gMatStackIndex]), 1076 G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH); 1077 gCurGraphNodeRoot = node; 1078 if (node->node.children != NULL) { 1079 geo_process_node_and_siblings(node->node.children); 1080 } 1081 gCurGraphNodeRoot = NULL; 1082 if (gShowDebugText) { 1083 print_text_fmt_int(180, 36, "MEM %d", 1084 gDisplayListHeap->totalSpace - gDisplayListHeap->usedSpace); 1085 } 1086 main_pool_free(gDisplayListHeap); 1087 } 1088 }