shadow.c (31794B)
1 #include <PR/ultratypes.h> 2 #include <PR/gbi.h> 3 #include <math.h> 4 5 #include "engine/math_util.h" 6 #include "engine/surface_collision.h" 7 #include "geo_misc.h" 8 #include "level_table.h" 9 #include "memory.h" 10 #include "object_list_processor.h" 11 #include "rendering_graph_node.h" 12 #include "segment2.h" 13 #include "shadow.h" 14 #include "sm64.h" 15 16 /** 17 * @file shadow.c 18 * This file implements a self-contained subsystem used to draw shadows. 19 */ 20 21 /** 22 * Encapsulation of information about a shadow. 23 */ 24 struct Shadow { 25 /* The (x, y, z) position of the object whose shadow this is. */ 26 f32 parentX; 27 f32 parentY; 28 f32 parentZ; 29 /* The y-position of the floor (or water or lava) underneath the object. */ 30 f32 floorHeight; 31 /* Initial (unmodified) size of the shadow. */ 32 f32 shadowScale; 33 /* (nx, ny, nz) normal vector of the floor underneath the object. */ 34 f32 floorNormalX; 35 f32 floorNormalY; 36 f32 floorNormalZ; 37 /* "originOffset" of the floor underneath the object. */ 38 f32 floorOriginOffset; 39 /* Angle describing "which way a marble would roll," in degrees. */ 40 f32 floorDownwardAngle; 41 /* Angle describing "how tilted the ground is" in degrees (-90 to 90). */ 42 f32 floorTilt; 43 /* Initial solidity of the shadow, from 0 to 255 (just an alpha value). */ 44 u8 solidity; 45 }; 46 47 /** 48 * Constant to indicate that a shadow should not be drawn. 49 * This is used to disable shadows during specific frames of Mario's 50 * animations. 51 */ 52 #define SHADOW_SOLIDITY_NO_SHADOW 0 53 /** 54 * Constant to indicate that a shadow's solidity has been pre-set by a previous 55 * function and should not be overwritten. 56 */ 57 #define SHADOW_SOILDITY_ALREADY_SET 1 58 /** 59 * Constant to indicate that a shadow's solidity has not yet been set. 60 */ 61 #define SHADOW_SOLIDITY_NOT_YET_SET 2 62 63 /** 64 * Constant to indicate any sort of circular shadow. 65 */ 66 #define SHADOW_SHAPE_CIRCLE 10 67 /** 68 * Constant to indicate any sort of rectangular shadow. 69 */ 70 #define SHADOW_SHAPE_SQUARE 20 71 72 /** 73 * Constant to indicate a shadow consists of 9 vertices. 74 */ 75 #define SHADOW_WITH_9_VERTS 0 76 /** 77 * Constant to indicate a shadow consists of 4 vertices. 78 */ 79 #define SHADOW_WITH_4_VERTS 1 80 81 /** 82 * A struct containing info about hardcoded rectangle shadows. 83 */ 84 typedef struct { 85 /* Half the width of the rectangle. */ 86 f32 halfWidth; 87 /* Half the length of the rectangle. */ 88 f32 halfLength; 89 /* Flag for if this shadow be smaller when its object is further away. */ 90 s8 scaleWithDistance; 91 } shadowRectangle; 92 93 /** 94 * An array consisting of all the hardcoded rectangle shadows in the game. 95 */ 96 shadowRectangle rectangles[2] = { 97 /* Shadow for Spindels. */ 98 { 360.0f, 230.0f, TRUE }, 99 /* Shadow for Whomps. */ 100 { 200.0f, 180.0f, TRUE } 101 }; 102 103 // See shadow.h for documentation. 104 s8 gShadowAboveWaterOrLava; 105 s8 gMarioOnIceOrCarpet; 106 s8 sMarioOnFlyingCarpet; 107 s16 sSurfaceTypeBelowShadow; 108 109 /** 110 * Let (oldZ, oldX) be the relative coordinates of a point on a rectangle, 111 * assumed to be centered at the origin on the standard SM64 X-Z plane. This 112 * function will update (newZ, newX) to equal the new coordinates of that point 113 * after a rotation equal to the yaw of the current graph node object. 114 */ 115 void rotate_rectangle(f32 *newZ, f32 *newX, f32 oldZ, f32 oldX) { 116 struct Object *obj = (struct Object *) gCurGraphNodeObject; 117 *newZ = oldZ * coss(obj->oFaceAngleYaw) - oldX * sins(obj->oFaceAngleYaw); 118 *newX = oldZ * sins(obj->oFaceAngleYaw) + oldX * coss(obj->oFaceAngleYaw); 119 } 120 121 /** 122 * Return atan2(a, b) in degrees. Note that the argument order is swapped from 123 * the standard atan2. 124 */ 125 f32 atan2_deg(f32 a, f32 b) { 126 return ((f32) atan2s(a, b) / 65535.0 * 360.0); 127 } 128 129 /** 130 * Shrink a shadow when its parent object is further from the floor, given the 131 * initial size of the shadow and the current distance. 132 */ 133 f32 scale_shadow_with_distance(f32 initial, f32 distFromFloor) { 134 f32 newScale; 135 136 if (distFromFloor <= 0.0) { 137 newScale = initial; 138 } else if (distFromFloor >= 600.0) { 139 newScale = initial * 0.5; 140 } else { 141 newScale = initial * (1.0 - (0.5 * distFromFloor / 600.0)); 142 } 143 144 return newScale; 145 } 146 147 /** 148 * Disable a shadow when its parent object is more than 600 units from the ground. 149 */ 150 f32 disable_shadow_with_distance(f32 shadowScale, f32 distFromFloor) { 151 if (distFromFloor >= 600.0) { 152 return 0.0f; 153 } else { 154 return shadowScale; 155 } 156 } 157 158 /** 159 * Dim a shadow when its parent object is further from the ground. 160 */ 161 u8 dim_shadow_with_distance(u8 solidity, f32 distFromFloor) { 162 f32 ret; 163 164 if (solidity < 121) { 165 return solidity; 166 } else if (distFromFloor <= 0.0) { 167 return solidity; 168 } else if (distFromFloor >= 600.0) { 169 return 120; 170 } else { 171 ret = ((120 - solidity) * distFromFloor) / 600.0 + (f32) solidity; 172 return ret; 173 } 174 } 175 176 /** 177 * Return the water level below a shadow, or 0 if the water level is below 178 * -10,000. 179 */ 180 f32 get_water_level_below_shadow(struct Shadow *s) { 181 f32 waterLevel = find_water_level(s->parentX, s->parentZ); 182 if (waterLevel < FLOOR_LOWER_LIMIT_SHADOW) { 183 return 0; 184 } else if (s->parentY >= waterLevel && s->floorHeight <= waterLevel) { 185 gShadowAboveWaterOrLava = TRUE; 186 return waterLevel; 187 } 188 //! @bug Missing return statement. This compiles to return `waterLevel` 189 //! incidentally. 190 #ifdef AVOID_UB 191 return waterLevel; 192 #endif 193 } 194 195 /** 196 * Initialize a shadow. Return 0 on success, 1 on failure. 197 * 198 * @param xPos,yPos,zPos Position of the parent object (not the shadow) 199 * @param shadowScale Diameter of the shadow 200 * @param overwriteSolidity Flag for whether the existing shadow solidity should 201 * be dimmed based on its distance to the floor 202 */ 203 s8 init_shadow(struct Shadow *s, f32 xPos, f32 yPos, f32 zPos, s16 shadowScale, u8 overwriteSolidity) { 204 f32 waterLevel; 205 f32 floorSteepness; 206 struct FloorGeometry *floorGeometry; 207 208 s->parentX = xPos; 209 s->parentY = yPos; 210 s->parentZ = zPos; 211 212 s->floorHeight = find_floor_height_and_data(s->parentX, s->parentY, s->parentZ, &floorGeometry); 213 214 if (gEnvironmentRegions != NULL) { 215 waterLevel = get_water_level_below_shadow(s); 216 } 217 if (gShadowAboveWaterOrLava) { 218 //! @bug Use of potentially undefined variable `waterLevel` 219 s->floorHeight = waterLevel; 220 221 // Assume that the water is flat. 222 s->floorNormalX = 0; 223 s->floorNormalY = 1.0; 224 s->floorNormalZ = 0; 225 s->floorOriginOffset = -waterLevel; 226 } else { 227 // Don't draw a shadow if the floor is lower than expected possible, 228 // or if the y-normal is negative (an unexpected result). 229 if (s->floorHeight < FLOOR_LOWER_LIMIT_SHADOW || floorGeometry->normalY <= 0.0) { 230 return 1; 231 } 232 233 s->floorNormalX = floorGeometry->normalX; 234 s->floorNormalY = floorGeometry->normalY; 235 s->floorNormalZ = floorGeometry->normalZ; 236 s->floorOriginOffset = floorGeometry->originOffset; 237 } 238 239 if (overwriteSolidity) { 240 s->solidity = dim_shadow_with_distance(overwriteSolidity, yPos - s->floorHeight); 241 } 242 243 s->shadowScale = scale_shadow_with_distance(shadowScale, yPos - s->floorHeight); 244 245 s->floorDownwardAngle = atan2_deg(s->floorNormalZ, s->floorNormalX); 246 247 floorSteepness = sqrtf(s->floorNormalX * s->floorNormalX + s->floorNormalZ * s->floorNormalZ); 248 249 // This if-statement avoids dividing by 0. 250 if (floorSteepness == 0.0) { 251 s->floorTilt = 0; 252 } else { 253 s->floorTilt = 90.0 - atan2_deg(floorSteepness, s->floorNormalY); 254 } 255 return 0; 256 } 257 258 /** 259 * Given a `vertexNum` from a shadow with nine vertices, update the 260 * texture coordinates corresponding to that vertex. That is: 261 * 0 = (-15, -15) 1 = (0, -15) 2 = (15, -15) 262 * 3 = (-15, 0) 4 = (0, 0) 5 = (15, 0) 263 * 6 = (-15, 15) 7 = (0, 15) 8 = (15, 15) 264 */ 265 void get_texture_coords_9_vertices(s8 vertexNum, s16 *textureX, s16 *textureY) { 266 *textureX = vertexNum % 3 * 15 - 15; 267 *textureY = vertexNum / 3 * 15 - 15; 268 } 269 270 /** 271 * Given a `vertexNum` from a shadow with four vertices, update the 272 * texture coordinates corresponding to that vertex. That is: 273 * 0 = (-15, -15) 1 = (15, -15) 274 * 2 = (-15, 15) 3 = (15, 15) 275 */ 276 void get_texture_coords_4_vertices(s8 vertexNum, s16 *textureX, s16 *textureY) { 277 *textureX = (vertexNum % 2) * 2 * 15 - 15; 278 *textureY = (vertexNum / 2) * 2 * 15 - 15; 279 } 280 281 /** 282 * Make a shadow's vertex at a position relative to its parent. 283 * 284 * @param vertices A preallocated display list for vertices 285 * @param index Index into `vertices` to insert the vertex 286 * @param relX,relY,relZ Vertex position relative to its parent object 287 * @param alpha Opacity of the vertex 288 * @param shadowVertexType One of SHADOW_WITH_9_VERTS or SHADOW_WITH_4_VERTS 289 */ 290 void make_shadow_vertex_at_xyz(Vtx *vertices, s8 index, f32 relX, f32 relY, f32 relZ, u8 alpha, 291 s8 shadowVertexType) { 292 s16 vtxX = round_float(relX); 293 s16 vtxY = round_float(relY); 294 s16 vtxZ = round_float(relZ); 295 s16 textureX, textureY; 296 297 switch (shadowVertexType) { 298 case SHADOW_WITH_9_VERTS: 299 get_texture_coords_9_vertices(index, &textureX, &textureY); 300 break; 301 case SHADOW_WITH_4_VERTS: 302 get_texture_coords_4_vertices(index, &textureX, &textureY); 303 break; 304 } 305 306 // Move the shadow up and over slightly while standing on a flying carpet. 307 if (sMarioOnFlyingCarpet) { 308 vtxX += 5; 309 vtxY += 5; 310 vtxZ += 5; 311 } 312 make_vertex( // shadows are black 313 vertices, index, vtxX, vtxY, vtxZ, textureX << 5, textureY << 5, 255, 255, 255, alpha 314 ); 315 } 316 317 /** 318 * Given an (x, z)-position close to a shadow, extrapolate the y-position 319 * according to the floor's normal vector. 320 */ 321 f32 extrapolate_vertex_y_position(struct Shadow s, f32 vtxX, f32 vtxZ) { 322 return -(s.floorNormalX * vtxX + s.floorNormalZ * vtxZ + s.floorOriginOffset) / s.floorNormalY; 323 } 324 325 /** 326 * Given a shadow vertex with the given `index`, return the corresponding texture 327 * coordinates ranging in the square with corners at (-1, -1), (1, -1), (-1, 1), 328 * and (1, 1) in the x-z plane. See `get_texture_coords_9_vertices()` and 329 * `get_texture_coords_4_vertices()`, which have similar functionality, but 330 * return 15 times these values. 331 */ 332 void get_vertex_coords(s8 index, s8 shadowVertexType, s8 *xCoord, s8 *zCoord) { 333 *xCoord = index % (3 - shadowVertexType) - 1; 334 *zCoord = index / (3 - shadowVertexType) - 1; 335 336 // This just corrects the 4-vertex case to have consistent results with the 337 // 9-vertex case. 338 if (shadowVertexType == SHADOW_WITH_4_VERTS) { 339 if (*xCoord == 0) { 340 *xCoord = 1; 341 } 342 if (*zCoord == 0) { 343 *zCoord = 1; 344 } 345 } 346 } 347 348 /** 349 * Populate `xPosVtx`, `yPosVtx`, and `zPosVtx` with the (x, y, z) position of the 350 * shadow vertex with the given index. If the shadow is to have 9 vertices, 351 * then each of those vertices is clamped down to the floor below it. Otherwise, 352 * in the 4 vertex case, the vertex positions are extrapolated from the center 353 * of the shadow. 354 * 355 * In practice, due to the if-statement in `make_shadow_vertex()`, the 9 356 * vertex and 4 vertex cases are identical, and the above-described clamping 357 * behavior is overwritten. 358 */ 359 void calculate_vertex_xyz(s8 index, struct Shadow s, f32 *xPosVtx, f32 *yPosVtx, f32 *zPosVtx, 360 s8 shadowVertexType) { 361 f32 tiltedScale = cosf(s.floorTilt * M_PI / 180.0) * s.shadowScale; 362 f32 downwardAngle = s.floorDownwardAngle * M_PI / 180.0; 363 f32 halfScale; 364 f32 halfTiltedScale; 365 s8 xCoordUnit; 366 s8 zCoordUnit; 367 struct FloorGeometry *dummy; 368 369 // This makes xCoordUnit and yCoordUnit each one of -1, 0, or 1. 370 get_vertex_coords(index, shadowVertexType, &xCoordUnit, &zCoordUnit); 371 372 halfScale = (xCoordUnit * s.shadowScale) / 2.0; 373 halfTiltedScale = (zCoordUnit * tiltedScale) / 2.0; 374 375 *xPosVtx = (halfTiltedScale * sinf(downwardAngle)) + (halfScale * cosf(downwardAngle)) + s.parentX; 376 *zPosVtx = (halfTiltedScale * cosf(downwardAngle)) - (halfScale * sinf(downwardAngle)) + s.parentZ; 377 378 if (gShadowAboveWaterOrLava) { 379 *yPosVtx = s.floorHeight; 380 } else { 381 switch (shadowVertexType) { 382 /** 383 * Note that this dichotomy is later overwritten in 384 * make_shadow_vertex(). 385 */ 386 case SHADOW_WITH_9_VERTS: 387 // Clamp this vertex's y-position to that of the floor directly 388 // below it, which may differ from the floor below the center 389 // vertex. 390 *yPosVtx = find_floor_height_and_data(*xPosVtx, s.parentY, *zPosVtx, &dummy); 391 break; 392 case SHADOW_WITH_4_VERTS: 393 // Do not clamp. Instead, extrapolate the y-position of this 394 // vertex based on the directly floor below the parent object. 395 *yPosVtx = extrapolate_vertex_y_position(s, *xPosVtx, *zPosVtx); 396 break; 397 } 398 } 399 } 400 401 /** 402 * Given a vertex's location, return the dot product of the 403 * position of that vertex (relative to the shadow's center) with the floor 404 * normal (at the shadow's center). 405 * 406 * Since it is a dot product, this returns 0 if these two vectors are 407 * perpendicular, meaning the ground is locally flat. It returns nonzero 408 * in most cases where `vtxY` is on a different floor triangle from the 409 * center vertex, as in the case with SHADOW_WITH_9_VERTS, which sets 410 * the y-value from `find_floor_height_and_data`. (See the bottom of 411 * `calculate_vertex_xyz`.) 412 */ 413 s16 floor_local_tilt(struct Shadow s, f32 vtxX, f32 vtxY, f32 vtxZ) { 414 f32 relX = vtxX - s.parentX; 415 f32 relY = vtxY - s.floorHeight; 416 f32 relZ = vtxZ - s.parentZ; 417 418 f32 ret = (relX * s.floorNormalX) + (relY * s.floorNormalY) + (relZ * s.floorNormalZ); 419 return ret; 420 } 421 422 /** 423 * Make a particular vertex from a shadow, calculating its position and solidity. 424 */ 425 void make_shadow_vertex(Vtx *vertices, s8 index, struct Shadow s, s8 shadowVertexType) { 426 f32 xPosVtx, yPosVtx, zPosVtx; 427 f32 relX, relY, relZ; 428 429 u8 solidity = s.solidity; 430 if (gShadowAboveWaterOrLava) { 431 solidity = 200; 432 } 433 434 calculate_vertex_xyz(index, s, &xPosVtx, &yPosVtx, &zPosVtx, shadowVertexType); 435 436 /** 437 * This is the hack that makes "SHADOW_WITH_9_VERTS" act identically to 438 * "SHADOW_WITH_4_VERTS" in the game; this same hack is disabled by the 439 * GameShark code in this video: https://youtu.be/MSIh4rtNGF0. The code in 440 * the video makes `extrapolate_vertex_y_position` return the same value as 441 * the last-called function that returns a float; in this case, that's 442 * `find_floor_height_and_data`, which this if-statement was designed to 443 * overwrite in the first place. Thus, this if-statement is disabled by that 444 * code. 445 * 446 * The last condition here means the y-position calculated previously 447 * was probably on a different floor triangle from the center vertex. 448 * The gShadowAboveWaterOrLava check is redundant, since `floor_local_tilt` 449 * will always be 0 over water or lava (since they are always flat). 450 */ 451 if (shadowVertexType == SHADOW_WITH_9_VERTS && !gShadowAboveWaterOrLava 452 && floor_local_tilt(s, xPosVtx, yPosVtx, zPosVtx) != 0) { 453 yPosVtx = extrapolate_vertex_y_position(s, xPosVtx, zPosVtx); 454 solidity = 0; 455 } 456 relX = xPosVtx - s.parentX; 457 relY = yPosVtx - s.parentY; 458 relZ = zPosVtx - s.parentZ; 459 460 make_shadow_vertex_at_xyz(vertices, index, relX, relY, relZ, solidity, shadowVertexType); 461 } 462 463 /** 464 * Add a shadow to the given display list. 465 */ 466 void add_shadow_to_display_list(Gfx *displayListHead, Vtx *verts, s8 shadowVertexType, s8 shadowShape) { 467 switch (shadowShape) { 468 case SHADOW_SHAPE_CIRCLE: 469 gSPDisplayList(displayListHead++, dl_shadow_circle); 470 break; 471 case SHADOW_SHAPE_SQUARE: 472 gSPDisplayList(displayListHead++, dl_shadow_square) break; 473 } 474 switch (shadowVertexType) { 475 case SHADOW_WITH_9_VERTS: 476 gSPVertex(displayListHead++, verts, 9, 0); 477 gSPDisplayList(displayListHead++, dl_shadow_9_verts); 478 break; 479 case SHADOW_WITH_4_VERTS: 480 gSPVertex(displayListHead++, verts, 4, 0); 481 gSPDisplayList(displayListHead++, dl_shadow_4_verts); 482 break; 483 } 484 gSPDisplayList(displayListHead++, dl_shadow_end); 485 gSPEndDisplayList(displayListHead); 486 } 487 488 /** 489 * Linearly interpolate a shadow's solidity between zero and finalSolidity 490 * depending on curr's relation to start and end. 491 */ 492 void linearly_interpolate_solidity_positive(struct Shadow *s, u8 finalSolidity, s16 curr, s16 start, 493 s16 end) { 494 if (curr >= 0 && curr < start) { 495 s->solidity = 0; 496 } else if (end < curr) { 497 s->solidity = finalSolidity; 498 } else { 499 s->solidity = (f32) finalSolidity * (curr - start) / (end - start); 500 } 501 } 502 503 /** 504 * Linearly interpolate a shadow's solidity between initialSolidity and zero 505 * depending on curr's relation to start and end. Note that if curr < start, 506 * the solidity will be zero. 507 */ 508 void linearly_interpolate_solidity_negative(struct Shadow *s, u8 initialSolidity, s16 curr, s16 start, 509 s16 end) { 510 // The curr < start case is not handled. Thus, if start != 0, this function 511 // will have the surprising behavior of hiding the shadow until start. 512 // This is not necessarily a bug, since this function is only used once, 513 // with start == 0. 514 if (curr >= start && end >= curr) { 515 s->solidity = ((f32) initialSolidity * (1.0 - (f32)(curr - start) / (end - start))); 516 } else { 517 s->solidity = 0; 518 } 519 } 520 521 /** 522 * Change a shadow's solidity based on the player's current animation frame. 523 */ 524 s8 correct_shadow_solidity_for_animations(s32 isLuigi, u8 initialSolidity, struct Shadow *shadow) { 525 struct Object *player; 526 s8 ret; 527 s16 animFrame; 528 529 switch (isLuigi) { 530 case 0: 531 player = gMarioObject; 532 break; 533 case 1: 534 /** 535 * This is evidence of a removed second player, likely Luigi. 536 * This variable lies in memory just after the gMarioObject and 537 * has the same type of shadow that Mario does. The `isLuigi` 538 * variable is never 1 in the game. Note that since this was a 539 * switch-case, not an if-statement, the programmers possibly 540 * intended there to be even more than 2 characters. 541 */ 542 player = gLuigiObject; 543 break; 544 } 545 546 animFrame = player->header.gfx.animInfo.animFrame; 547 switch (player->header.gfx.animInfo.animID) { 548 case MARIO_ANIM_IDLE_ON_LEDGE: 549 ret = SHADOW_SOLIDITY_NO_SHADOW; 550 break; 551 case MARIO_ANIM_FAST_LEDGE_GRAB: 552 linearly_interpolate_solidity_positive(shadow, initialSolidity, animFrame, 5, 14); 553 ret = SHADOW_SOILDITY_ALREADY_SET; 554 break; 555 case MARIO_ANIM_SLOW_LEDGE_GRAB: 556 linearly_interpolate_solidity_positive(shadow, initialSolidity, animFrame, 21, 33); 557 ret = SHADOW_SOILDITY_ALREADY_SET; 558 break; 559 case MARIO_ANIM_CLIMB_DOWN_LEDGE: 560 linearly_interpolate_solidity_negative(shadow, initialSolidity, animFrame, 0, 5); 561 ret = SHADOW_SOILDITY_ALREADY_SET; 562 break; 563 default: 564 ret = SHADOW_SOLIDITY_NOT_YET_SET; 565 break; 566 } 567 return ret; 568 } 569 570 /** 571 * Slightly change the height of a shadow in levels with lava. 572 */ 573 void correct_lava_shadow_height(struct Shadow *s) { 574 if (gCurrLevelNum == LEVEL_BITFS && sSurfaceTypeBelowShadow == SURFACE_BURNING) { 575 if (s->floorHeight < -3000.0) { 576 s->floorHeight = -3062.0; 577 gShadowAboveWaterOrLava = TRUE; 578 } else if (s->floorHeight > 3400.0) { 579 s->floorHeight = 3492.0; 580 gShadowAboveWaterOrLava = TRUE; 581 } 582 } else if (gCurrLevelNum == LEVEL_LLL && gCurrAreaIndex == 1 583 && sSurfaceTypeBelowShadow == SURFACE_BURNING) { 584 s->floorHeight = 5.0; 585 gShadowAboveWaterOrLava = TRUE; 586 } 587 } 588 589 /** 590 * Create a shadow under a player, correcting that shadow's opacity during 591 * appropriate animations and other states. 592 */ 593 Gfx *create_shadow_player(f32 xPos, f32 yPos, f32 zPos, s16 shadowScale, u8 solidity, s32 isLuigi) { 594 Vtx *verts; 595 Gfx *displayList; 596 struct Shadow shadow; 597 s8 ret; 598 s32 i; 599 600 // Update global variables about whether Mario is on a flying carpet. 601 if (gCurrLevelNum == LEVEL_RR && sSurfaceTypeBelowShadow != SURFACE_DEATH_PLANE) { 602 switch (gFlyingCarpetState) { 603 case FLYING_CARPET_MOVING_WITHOUT_MARIO: 604 gMarioOnIceOrCarpet = 1; 605 sMarioOnFlyingCarpet = 1; 606 break; 607 case FLYING_CARPET_MOVING_WITH_MARIO: 608 gMarioOnIceOrCarpet = 1; 609 break; 610 } 611 } 612 613 switch (correct_shadow_solidity_for_animations(isLuigi, solidity, &shadow)) { 614 case SHADOW_SOLIDITY_NO_SHADOW: 615 return NULL; 616 break; 617 case SHADOW_SOILDITY_ALREADY_SET: 618 ret = init_shadow(&shadow, xPos, yPos, zPos, shadowScale, /* overwriteSolidity */ 0); 619 break; 620 case SHADOW_SOLIDITY_NOT_YET_SET: 621 ret = init_shadow(&shadow, xPos, yPos, zPos, shadowScale, solidity); 622 break; 623 } 624 if (ret != 0) { 625 return NULL; 626 } 627 628 verts = alloc_display_list(9 * sizeof(Vtx)); 629 displayList = alloc_display_list(5 * sizeof(Gfx)); 630 if (verts == NULL || displayList == NULL) { 631 return NULL; 632 } 633 634 correct_lava_shadow_height(&shadow); 635 636 for (i = 0; i < 9; i++) { 637 make_shadow_vertex(verts, i, shadow, SHADOW_WITH_9_VERTS); 638 } 639 add_shadow_to_display_list(displayList, verts, SHADOW_WITH_9_VERTS, SHADOW_SHAPE_CIRCLE); 640 return displayList; 641 } 642 643 /** 644 * Create a circular shadow composed of 9 vertices. 645 */ 646 Gfx *create_shadow_circle_9_verts(f32 xPos, f32 yPos, f32 zPos, s16 shadowScale, u8 solidity) { 647 Vtx *verts; 648 Gfx *displayList; 649 struct Shadow shadow; 650 s32 i; 651 652 if (init_shadow(&shadow, xPos, yPos, zPos, shadowScale, solidity) != 0) { 653 return NULL; 654 } 655 656 verts = alloc_display_list(9 * sizeof(Vtx)); 657 displayList = alloc_display_list(5 * sizeof(Gfx)); 658 659 if (verts == NULL || displayList == NULL) { 660 return 0; 661 } 662 for (i = 0; i < 9; i++) { 663 make_shadow_vertex(verts, i, shadow, SHADOW_WITH_9_VERTS); 664 } 665 add_shadow_to_display_list(displayList, verts, SHADOW_WITH_9_VERTS, SHADOW_SHAPE_CIRCLE); 666 return displayList; 667 } 668 669 /** 670 * Create a circular shadow composed of 4 vertices. 671 */ 672 Gfx *create_shadow_circle_4_verts(f32 xPos, f32 yPos, f32 zPos, s16 shadowScale, u8 solidity) { 673 Vtx *verts; 674 Gfx *displayList; 675 struct Shadow shadow; 676 s32 i; 677 678 if (init_shadow(&shadow, xPos, yPos, zPos, shadowScale, solidity) != 0) { 679 return NULL; 680 } 681 682 verts = alloc_display_list(4 * sizeof(Vtx)); 683 displayList = alloc_display_list(5 * sizeof(Gfx)); 684 685 if (verts == NULL || displayList == NULL) { 686 return 0; 687 } 688 689 for (i = 0; i < 4; i++) { 690 make_shadow_vertex(verts, i, shadow, SHADOW_WITH_4_VERTS); 691 } 692 add_shadow_to_display_list(displayList, verts, SHADOW_WITH_4_VERTS, SHADOW_SHAPE_CIRCLE); 693 return displayList; 694 } 695 696 /** 697 * Create a circular shadow composed of 4 vertices and assume that the ground 698 * underneath it is totally flat. 699 */ 700 Gfx *create_shadow_circle_assuming_flat_ground(f32 xPos, f32 yPos, f32 zPos, s16 shadowScale, 701 u8 solidity) { 702 Vtx *verts; 703 Gfx *displayList; 704 struct FloorGeometry *dummy; // only for calling find_floor_height_and_data 705 f32 distBelowFloor; 706 f32 floorHeight = find_floor_height_and_data(xPos, yPos, zPos, &dummy); 707 f32 radius = shadowScale / 2; 708 709 if (floorHeight < FLOOR_LOWER_LIMIT_SHADOW) { 710 return NULL; 711 } else { 712 distBelowFloor = floorHeight - yPos; 713 } 714 715 verts = alloc_display_list(4 * sizeof(Vtx)); 716 displayList = alloc_display_list(5 * sizeof(Gfx)); 717 718 if (verts == NULL || displayList == NULL) { 719 return 0; 720 } 721 722 make_shadow_vertex_at_xyz(verts, 0, -radius, distBelowFloor, -radius, solidity, 1); 723 make_shadow_vertex_at_xyz(verts, 1, radius, distBelowFloor, -radius, solidity, 1); 724 make_shadow_vertex_at_xyz(verts, 2, -radius, distBelowFloor, radius, solidity, 1); 725 make_shadow_vertex_at_xyz(verts, 3, radius, distBelowFloor, radius, solidity, 1); 726 727 add_shadow_to_display_list(displayList, verts, SHADOW_WITH_4_VERTS, SHADOW_SHAPE_CIRCLE); 728 return displayList; 729 } 730 731 /** 732 * Create a rectangular shadow composed of 4 vertices. This assumes the ground 733 * underneath the shadow is totally flat. 734 */ 735 Gfx *create_shadow_rectangle(f32 halfWidth, f32 halfLength, f32 relY, u8 solidity) { 736 Vtx *verts = alloc_display_list(4 * sizeof(Vtx)); 737 Gfx *displayList = alloc_display_list(5 * sizeof(Gfx)); 738 f32 frontLeftX, frontLeftZ, frontRightX, frontRightZ, backLeftX, backLeftZ, backRightX, backRightZ; 739 740 if (verts == NULL || displayList == NULL) { 741 return NULL; 742 } 743 744 // Rotate the shadow based on the parent object's face angle. 745 rotate_rectangle(&frontLeftZ, &frontLeftX, -halfLength, -halfWidth); 746 rotate_rectangle(&frontRightZ, &frontRightX, -halfLength, halfWidth); 747 rotate_rectangle(&backLeftZ, &backLeftX, halfLength, -halfWidth); 748 rotate_rectangle(&backRightZ, &backRightX, halfLength, halfWidth); 749 750 make_shadow_vertex_at_xyz(verts, 0, frontLeftX, relY, frontLeftZ, solidity, 1); 751 make_shadow_vertex_at_xyz(verts, 1, frontRightX, relY, frontRightZ, solidity, 1); 752 make_shadow_vertex_at_xyz(verts, 2, backLeftX, relY, backLeftZ, solidity, 1); 753 make_shadow_vertex_at_xyz(verts, 3, backRightX, relY, backRightZ, solidity, 1); 754 755 add_shadow_to_display_list(displayList, verts, SHADOW_WITH_4_VERTS, SHADOW_SHAPE_SQUARE); 756 return displayList; 757 } 758 759 /** 760 * Populate `shadowHeight` and `solidity` appropriately; the default solidity 761 * value is 200. Return 0 if a shadow should be drawn, 1 if not. 762 */ 763 s32 get_shadow_height_solidity(f32 xPos, f32 yPos, f32 zPos, f32 *shadowHeight, u8 *solidity) { 764 struct FloorGeometry *dummy; 765 f32 waterLevel; 766 *shadowHeight = find_floor_height_and_data(xPos, yPos, zPos, &dummy); 767 768 if (*shadowHeight < FLOOR_LOWER_LIMIT_SHADOW) { 769 return 1; 770 } else { 771 waterLevel = find_water_level(xPos, zPos); 772 773 if (waterLevel < FLOOR_LOWER_LIMIT_SHADOW) { 774 // Dead if-statement. There may have been an assert here. 775 } else if (yPos >= waterLevel && waterLevel >= *shadowHeight) { 776 gShadowAboveWaterOrLava = TRUE; 777 *shadowHeight = waterLevel; 778 *solidity = 200; 779 } 780 } 781 return 0; 782 } 783 784 /** 785 * Create a square shadow composed of 4 vertices. 786 */ 787 Gfx *create_shadow_square(f32 xPos, f32 yPos, f32 zPos, s16 shadowScale, u8 solidity, s8 shadowType) { 788 f32 shadowHeight; 789 f32 distFromShadow; 790 f32 shadowRadius; 791 792 if (get_shadow_height_solidity(xPos, yPos, zPos, &shadowHeight, &solidity) != 0) { 793 return NULL; 794 } 795 796 distFromShadow = yPos - shadowHeight; 797 switch (shadowType) { 798 case SHADOW_SQUARE_PERMANENT: 799 shadowRadius = shadowScale / 2; 800 break; 801 case SHADOW_SQUARE_SCALABLE: 802 shadowRadius = scale_shadow_with_distance(shadowScale, distFromShadow) / 2.0; 803 break; 804 case SHADOW_SQUARE_TOGGLABLE: 805 shadowRadius = disable_shadow_with_distance(shadowScale, distFromShadow) / 2.0; 806 break; 807 default: 808 return NULL; 809 } 810 811 return create_shadow_rectangle(shadowRadius, shadowRadius, -distFromShadow, solidity); 812 } 813 814 /** 815 * Create a rectangular shadow whose parameters have been hardcoded in the 816 * `rectangles` array. 817 */ 818 Gfx *create_shadow_hardcoded_rectangle(f32 xPos, f32 yPos, f32 zPos, UNUSED s16 shadowScale, 819 u8 solidity, s8 shadowType) { 820 f32 shadowHeight; 821 f32 distFromShadow; 822 f32 halfWidth; 823 f32 halfLength; 824 s8 idx = shadowType - SHADOW_RECTANGLE_HARDCODED_OFFSET; 825 826 if (get_shadow_height_solidity(xPos, yPos, zPos, &shadowHeight, &solidity) != 0) { 827 return NULL; 828 } 829 830 distFromShadow = yPos - shadowHeight; 831 /** 832 * Note that idx could be negative or otherwise out of the bounds of 833 * the `rectangles` array. In practice, it never is, because this was 834 * only used twice. 835 */ 836 if (rectangles[idx].scaleWithDistance == TRUE) { 837 halfWidth = scale_shadow_with_distance(rectangles[idx].halfWidth, distFromShadow); 838 halfLength = scale_shadow_with_distance(rectangles[idx].halfLength, distFromShadow); 839 } else { 840 // This code is never used because the third element of the rectangle 841 // struct is always TRUE. 842 halfWidth = rectangles[idx].halfWidth; 843 halfLength = rectangles[idx].halfLength; 844 } 845 return create_shadow_rectangle(halfWidth, halfLength, -distFromShadow, solidity); 846 } 847 848 /** 849 * Create a shadow at the absolute position given, with the given parameters. 850 * Return a pointer to the display list representing the shadow. 851 */ 852 Gfx *create_shadow_below_xyz(f32 xPos, f32 yPos, f32 zPos, s16 shadowScale, u8 shadowSolidity, 853 s8 shadowType) { 854 Gfx *displayList = NULL; 855 struct Surface *pfloor; 856 find_floor(xPos, yPos, zPos, &pfloor); 857 858 gShadowAboveWaterOrLava = FALSE; 859 gMarioOnIceOrCarpet = 0; 860 sMarioOnFlyingCarpet = 0; 861 if (pfloor != NULL) { 862 if (pfloor->type == SURFACE_ICE) { 863 gMarioOnIceOrCarpet = 1; 864 } 865 sSurfaceTypeBelowShadow = pfloor->type; 866 } 867 switch (shadowType) { 868 case SHADOW_CIRCLE_9_VERTS: 869 displayList = create_shadow_circle_9_verts(xPos, yPos, zPos, shadowScale, shadowSolidity); 870 break; 871 case SHADOW_CIRCLE_4_VERTS: 872 displayList = create_shadow_circle_4_verts(xPos, yPos, zPos, shadowScale, shadowSolidity); 873 break; 874 case SHADOW_CIRCLE_4_VERTS_FLAT_UNUSED: // unused shadow type 875 displayList = create_shadow_circle_assuming_flat_ground(xPos, yPos, zPos, shadowScale, 876 shadowSolidity); 877 break; 878 case SHADOW_SQUARE_PERMANENT: 879 displayList = 880 create_shadow_square(xPos, yPos, zPos, shadowScale, shadowSolidity, shadowType); 881 break; 882 case SHADOW_SQUARE_SCALABLE: 883 displayList = 884 create_shadow_square(xPos, yPos, zPos, shadowScale, shadowSolidity, shadowType); 885 break; 886 case SHADOW_SQUARE_TOGGLABLE: 887 displayList = 888 create_shadow_square(xPos, yPos, zPos, shadowScale, shadowSolidity, shadowType); 889 break; 890 case SHADOW_CIRCLE_PLAYER: 891 displayList = create_shadow_player(xPos, yPos, zPos, shadowScale, shadowSolidity, 892 /* isLuigi */ FALSE); 893 break; 894 default: 895 displayList = create_shadow_hardcoded_rectangle(xPos, yPos, zPos, shadowScale, 896 shadowSolidity, shadowType); 897 break; 898 } 899 return displayList; 900 }