moving_texture.c (39729B)
1 #include <ultra64.h> 2 3 #include "sm64.h" 4 #include "moving_texture.h" 5 #include "area.h" 6 #include "camera.h" 7 #include "rendering_graph_node.h" 8 #include "engine/math_util.h" 9 #include "memory.h" 10 #include "save_file.h" 11 #include "segment2.h" 12 #include "engine/surface_collision.h" 13 #include "geo_misc.h" 14 #include "rendering_graph_node.h" 15 #include "object_list_processor.h" 16 17 /** 18 * This file contains functions for generating display lists with moving textures 19 * (abbreviated movtex). This is used for water, sand, haze, mist and treadmills. 20 * Each mesh using this system has the animated vertices stored as an array of shorts. 21 * The first entry is the texture movement speed. After that the vertices are stored 22 * in one of two layouts: one without per-vertex color attributes and one with. 23 * [speed, v0(x,y,z, s,t) , v1(x,y,z, s,t) , ...] 24 * [speed, v0(x,y,z, r,g,b s,t), v1(x,y,z, r,g,b s,t), ...] 25 * x, y, z = vertex position as integers 26 * s, t = texture coordinates as 6.10 fixed point number. That means coordinates in 27 * range [0, 1024] are a unique part of the image, after that it repeats the image. 28 * 29 * The first vertex 'v0' is special because all subsequent vertices inherit its 30 * texture offset. So to animate e.g. a treadmill, the speed component arr[0] is 31 * simply added to the s component arr[7] every frame and the texture scrolls 32 * horizontally over the entire mesh without changing the rest of the array. 33 * Note that while the system allows many kinds of vertex animations, in 34 * practice the only animation used is horizontally scrolling textures. 35 * 36 * After updating the base mesh, the vertices are converted to the format the RSP 37 * understands and a display list is generated. The RSP can buffer 16 vertices at 38 * a time, and this code assumes everything fits in one buffer, so every moving 39 * texture mesh must have at most 16 vertices. As a result some meshes are split 40 * up into multiple parts, like the sand pathway inside the pyramid which has 3 41 * parts. The water stream in the Cavern of the Metal Cap fits in one mesh. 42 * 43 * Apart from this general system, there is also a simpler system for flat 44 * quads with a rotating texture. This is often used for water, but also 45 * for mist, toxic haze and lava inside the volcano. One quad is described 46 * by the struct MovtexQuad, and multiple MovtexQuads form a MovtexQuadCollection. 47 * A geo node has an id that corresponds to the id of a certain MovtexQuadCollection, 48 * which will then be matched with the id of entries in gEnvironmentRegions to get the 49 * y-position. The x and z coordinates are stored in the MovtexQuads themself, 50 * so the water rectangle is separate from the actually drawn rectangle. 51 */ 52 53 // First entry in array is texture movement speed for both layouts 54 #define MOVTEX_ATTR_SPEED 0 55 56 // Different layouts for vertices 57 #define MOVTEX_LAYOUT_NOCOLOR 0 58 #define MOVTEX_LAYOUT_COLORED 1 59 60 // Attributes for movtex vertices 61 #define MOVTEX_ATTR_X 1 62 #define MOVTEX_ATTR_Y 2 63 #define MOVTEX_ATTR_Z 3 64 65 // For MOVTEX_LAYOUT_NOCOLOR only 66 #define MOVTEX_ATTR_NOCOLOR_S 4 67 #define MOVTEX_ATTR_NOCOLOR_T 5 68 69 // For MOVTEX_LAYOUT_COLORED only 70 #define MOVTEX_ATTR_COLORED_R 4 71 #define MOVTEX_ATTR_COLORED_G 5 72 #define MOVTEX_ATTR_COLORED_B 6 73 #define MOVTEX_ATTR_COLORED_S 7 74 #define MOVTEX_ATTR_COLORED_T 8 75 76 /** 77 * An object containing all info for a mesh with moving textures. 78 * Contains the vertices that are animated, but also the display list which 79 * determines the connectivity, as well as the texture, texture blend color 80 * and drawing layer. 81 */ 82 struct MovtexObject { 83 /// number that geo nodes have as parameter to refer to this mesh 84 u32 geoId; 85 /// which texture to use for this mesh, index into gMovtexIdToTexture 86 s32 textureId; 87 /// amount of moving vertices 88 s32 vtx_count; 89 /// segmented address to movtex mesh with vertices 90 void *movtexVerts; 91 /// display list inserted before moving triangles 92 Gfx *beginDl; 93 /// display list inserted after moving triangles 94 Gfx *endDl; 95 /// display list with the actual moving texture triangles. 96 /// Assumes the animated vertices are buffered and correct texture is set 97 Gfx *triDl; 98 // if the list does not have per-vertex colors, all vertices have these colors 99 u8 r; /// red 100 u8 g; /// green 101 u8 b; /// blue 102 u8 a; /// alpha 103 s32 layer; /// the drawing layer for this mesh 104 }; 105 106 /// Counters to make textures move iff the game is not paused. 107 s16 gMovtexCounter = 1; 108 s16 gMovtexCounterPrev = 0; 109 110 // Vertex colors for rectangles. Used to give mist a tint 111 #define MOVTEX_VTX_COLOR_DEFAULT 0 // no tint (white vertex colors) 112 #define MOVTEX_VTX_COLOR_YELLOW 1 // used for Hazy Maze Cave toxic haze 113 #define MOVTEX_VTX_COLOR_RED 2 // used for Shifting Sand Land around the Tox box maze 114 115 s8 gMovtexVtxColor = MOVTEX_VTX_COLOR_DEFAULT; 116 117 /// The height at which Mario entered the last painting. Used for Wet-Dry World only. 118 float gPaintingMarioYEntry = 0.0f; 119 120 /// Variable to ensure the initial Wet-Dry World water level is set only once 121 s32 gWDWWaterLevelSet = FALSE; 122 123 extern u8 ssl_quicksand[]; 124 extern u8 ssl_pyramid_sand[]; 125 extern u8 ttc_yellow_triangle[]; 126 127 /** 128 * An array for converting a movtex texture id to a pointer that can 129 * be passed to gDPSetTextureImage. 130 */ 131 u8 *gMovtexIdToTexture[] = { texture_waterbox_water, texture_waterbox_mist, 132 texture_waterbox_jrb_water, texture_waterbox_unknown_water, 133 texture_waterbox_lava, ssl_quicksand, 134 ssl_pyramid_sand, ttc_yellow_triangle }; 135 136 extern Gfx castle_grounds_dl_waterfall[]; 137 extern s16 castle_grounds_movtex_tris_waterfall[]; 138 extern s16 ssl_movtex_tris_pyramid_sand_pathway_front[]; 139 extern Gfx ssl_dl_pyramid_sand_pathway_begin[]; 140 extern Gfx ssl_dl_pyramid_sand_pathway_end[]; 141 extern Gfx ssl_dl_pyramid_sand_pathway_front_end[]; 142 extern s16 ssl_movtex_tris_pyramid_sand_pathway_floor[]; 143 extern Gfx ssl_dl_pyramid_sand_pathway_floor_begin[]; 144 extern Gfx ssl_dl_pyramid_sand_pathway_floor_end[]; 145 extern s16 ssl_movtex_tris_pyramid_sand_pathway_side[]; 146 extern Gfx ssl_dl_pyramid_sand_pathway_side_end[]; 147 extern s16 bitfs_movtex_tris_lava_first_section[]; 148 extern Gfx bitfs_dl_lava_sections[]; 149 extern s16 bitfs_movtex_tris_lava_second_section[]; 150 extern s16 bitfs_movtex_tris_lava_floor[]; 151 extern Gfx bitfs_dl_lava_floor[]; 152 extern s16 lll_movtex_tris_lava_floor[]; 153 extern Gfx lll_dl_lava_floor[]; 154 extern s16 lll_movtex_tris_lavafall_volcano[]; 155 extern Gfx lll_dl_lavafall_volcano[]; 156 extern s16 cotmc_movtex_tris_water[]; 157 extern Gfx cotmc_dl_water_begin[]; 158 extern Gfx cotmc_dl_water_end[]; 159 extern Gfx cotmc_dl_water[]; 160 extern s16 ttm_movtex_tris_begin_waterfall[]; 161 extern Gfx ttm_dl_waterfall[]; 162 extern s16 ttm_movtex_tris_end_waterfall[]; 163 extern s16 ttm_movtex_tris_begin_puddle_waterfall[]; 164 extern Gfx ttm_dl_bottom_waterfall[]; 165 extern s16 ttm_movtex_tris_end_puddle_waterfall[]; 166 extern s16 ttm_movtex_tris_puddle_waterfall[]; 167 extern Gfx ttm_dl_puddle_waterfall[]; 168 extern s16 ssl_movtex_tris_pyramid_quicksand[]; 169 extern Gfx ssl_dl_quicksand_begin[]; 170 extern Gfx ssl_dl_quicksand_end[]; 171 extern Gfx ssl_dl_pyramid_quicksand[]; 172 extern s16 ssl_movtex_tris_pyramid_corners_quicksand[]; 173 extern Gfx ssl_dl_pyramid_corners_quicksand[]; 174 extern s16 ssl_movtex_tris_sides_quicksand[]; 175 extern Gfx ssl_dl_sides_quicksand[]; 176 extern s16 ttc_movtex_tris_big_surface_treadmill[]; 177 extern Gfx ttc_dl_surface_treadmill_begin[]; 178 extern Gfx ttc_dl_surface_treadmill_end[]; 179 extern Gfx ttc_dl_surface_treadmill[]; 180 extern s16 ttc_movtex_tris_small_surface_treadmill[]; 181 extern s16 ssl_movtex_tris_quicksand_pit[]; 182 extern Gfx ssl_dl_quicksand_pit_begin[]; 183 extern Gfx ssl_dl_quicksand_pit_end[]; 184 extern Gfx ssl_dl_quicksand_pit[]; 185 extern s16 ssl_movtex_tris_pyramid_quicksand_pit[]; 186 extern Gfx ssl_dl_pyramid_quicksand_pit_begin[]; 187 extern Gfx ssl_dl_pyramid_quicksand_pit_end[]; 188 189 /** 190 * MovtexObjects that have no color attributes per vertex (though the mesh 191 * as a whole can have a blend color). 192 */ 193 struct MovtexObject gMovtexNonColored[] = { 194 // Inside the pyramid there is a sand pathway with the 5 secrets on it. 195 // pathway_front is the highest 'sand fall', pathway_floor is the horizontal 196 // sand stream and pathway_side is the lower 'sand fall'. 197 { MOVTEX_PYRAMID_SAND_PATHWAY_FRONT, TEX_PYRAMID_SAND_SSL, 8, 198 ssl_movtex_tris_pyramid_sand_pathway_front, ssl_dl_pyramid_sand_pathway_begin, 199 ssl_dl_pyramid_sand_pathway_end, ssl_dl_pyramid_sand_pathway_front_end, 0xff, 0xff, 0xff, 0xff, 200 LAYER_TRANSPARENT_INTER }, 201 { MOVTEX_PYRAMID_SAND_PATHWAY_FLOOR, TEX_PYRAMID_SAND_SSL, 8, 202 ssl_movtex_tris_pyramid_sand_pathway_floor, ssl_dl_pyramid_sand_pathway_floor_begin, 203 ssl_dl_pyramid_sand_pathway_floor_end, ssl_dl_pyramid_sand_pathway_front_end, 0xff, 0xff, 0xff, 204 0xff, LAYER_OPAQUE_INTER }, 205 { MOVTEX_PYRAMID_SAND_PATHWAY_SIDE, TEX_PYRAMID_SAND_SSL, 6, 206 ssl_movtex_tris_pyramid_sand_pathway_side, ssl_dl_pyramid_sand_pathway_begin, 207 ssl_dl_pyramid_sand_pathway_end, ssl_dl_pyramid_sand_pathway_side_end, 0xff, 0xff, 0xff, 0xff, 208 LAYER_TRANSPARENT_INTER }, 209 210 // The waterfall outside the castle 211 { MOVTEX_CASTLE_WATERFALL, TEXTURE_WATER, 15, castle_grounds_movtex_tris_waterfall, 212 dl_waterbox_rgba16_begin, dl_waterbox_end, castle_grounds_dl_waterfall, 0xff, 0xff, 0xff, 0xb4, 213 LAYER_TRANSPARENT_INTER }, 214 215 // Bowser in the Fire Sea has lava at 3 heights, lava_floor is the lowest 216 // and lava_second_section is the highest 217 { MOVTEX_BITFS_LAVA_FIRST, TEXTURE_LAVA, 4, bitfs_movtex_tris_lava_first_section, 218 dl_waterbox_rgba16_begin, dl_waterbox_end, bitfs_dl_lava_sections, 0xff, 0xff, 0xff, 0xff, 219 LAYER_OPAQUE }, 220 { MOVTEX_BITFS_LAVA_SECOND, TEXTURE_LAVA, 4, bitfs_movtex_tris_lava_second_section, 221 dl_waterbox_rgba16_begin, dl_waterbox_end, bitfs_dl_lava_sections, 0xff, 0xff, 0xff, 0xb4, 222 LAYER_TRANSPARENT }, 223 { MOVTEX_BITFS_LAVA_FLOOR, TEXTURE_LAVA, 9, bitfs_movtex_tris_lava_floor, dl_waterbox_rgba16_begin, 224 dl_waterbox_end, bitfs_dl_lava_floor, 0xff, 0xff, 0xff, 0xb4, LAYER_TRANSPARENT }, 225 226 // Lava floor in Lethal Lava Land and the lava fall in the volcano 227 //! Note that the lava floor in the volcano is actually a quad. 228 // The quad collection index LLL_MOVTEX_VOLCANO_FLOOR_LAVA is actually 229 // 2 | MOVTEX_AREA_LLL, suggesting that the lava floor of LLL used to be a 230 // quad too, with index 1. 231 // It was probably too large however, resulting in overflowing texture 232 // coordinates or other artifacts, so they converted it to a movtex 233 // mesh with 9 vertices, subdividing the rectangle into 4 smaller ones. 234 { MOVTEX_LLL_LAVA_FLOOR, TEXTURE_LAVA, 9, lll_movtex_tris_lava_floor, dl_waterbox_rgba16_begin, 235 dl_waterbox_end, lll_dl_lava_floor, 0xff, 0xff, 0xff, 0xc8, LAYER_TRANSPARENT }, 236 { MOVTEX_VOLCANO_LAVA_FALL, TEXTURE_LAVA, 16, lll_movtex_tris_lavafall_volcano, 237 dl_waterbox_rgba16_begin, dl_waterbox_end, lll_dl_lavafall_volcano, 0xff, 0xff, 0xff, 0xb4, 238 LAYER_TRANSPARENT_INTER }, 239 240 // Cavern of the metal Cap has a waterfall source above the switch platform, 241 // the stream, around the switch, and the waterfall that's the same as the one 242 // outside the castle. They are all part of the same mesh. 243 { MOVTEX_COTMC_WATER, TEXTURE_WATER, 14, cotmc_movtex_tris_water, cotmc_dl_water_begin, 244 cotmc_dl_water_end, cotmc_dl_water, 0xff, 0xff, 0xff, 0xb4, LAYER_TRANSPARENT_INTER }, 245 246 // Tall, Tall mountain has water going from the top to the bottom of the mountain. 247 { MOVTEX_TTM_BEGIN_WATERFALL, TEXTURE_WATER, 6, ttm_movtex_tris_begin_waterfall, 248 dl_waterbox_rgba16_begin, dl_waterbox_end, ttm_dl_waterfall, 0xff, 0xff, 0xff, 0xb4, 249 LAYER_TRANSPARENT }, 250 { MOVTEX_TTM_END_WATERFALL, TEXTURE_WATER, 6, ttm_movtex_tris_end_waterfall, 251 dl_waterbox_rgba16_begin, dl_waterbox_end, ttm_dl_waterfall, 0xff, 0xff, 0xff, 0xb4, 252 LAYER_TRANSPARENT }, 253 { MOVTEX_TTM_BEGIN_PUDDLE_WATERFALL, TEXTURE_WATER, 4, ttm_movtex_tris_begin_puddle_waterfall, 254 dl_waterbox_rgba16_begin, dl_waterbox_end, ttm_dl_bottom_waterfall, 0xff, 0xff, 0xff, 0xb4, 255 LAYER_TRANSPARENT_INTER }, 256 { MOVTEX_TTM_END_PUDDLE_WATERFALL, TEXTURE_WATER, 4, ttm_movtex_tris_end_puddle_waterfall, 257 dl_waterbox_rgba16_begin, dl_waterbox_end, ttm_dl_bottom_waterfall, 0xff, 0xff, 0xff, 0xb4, 258 LAYER_TRANSPARENT_INTER }, 259 { MOVTEX_TTM_PUDDLE_WATERFALL, TEXTURE_WATER, 8, ttm_movtex_tris_puddle_waterfall, 260 dl_waterbox_rgba16_begin, dl_waterbox_end, ttm_dl_puddle_waterfall, 0xff, 0xff, 0xff, 0xb4, 261 LAYER_TRANSPARENT_INTER }, 262 { 0x00000000, 0x00000000, 0, NULL, NULL, NULL, NULL, 0x00, 0x00, 0x00, 0x00, 0x00000000 }, 263 }; 264 265 /** 266 * MovtexObjects that have color attributes per vertex. 267 */ 268 struct MovtexObject gMovtexColored[] = { 269 { MOVTEX_SSL_PYRAMID_SIDE, TEX_QUICKSAND_SSL, 12, ssl_movtex_tris_pyramid_quicksand, 270 ssl_dl_quicksand_begin, ssl_dl_quicksand_end, ssl_dl_pyramid_quicksand, 0xff, 0xff, 0xff, 0xff, 271 LAYER_OPAQUE }, 272 { MOVTEX_SSL_PYRAMID_CORNER, TEX_QUICKSAND_SSL, 16, ssl_movtex_tris_pyramid_corners_quicksand, 273 ssl_dl_quicksand_begin, ssl_dl_quicksand_end, ssl_dl_pyramid_corners_quicksand, 0xff, 0xff, 0xff, 274 0xff, LAYER_OPAQUE }, 275 { MOVTEX_SSL_COURSE_EDGE, TEX_QUICKSAND_SSL, 15, ssl_movtex_tris_sides_quicksand, 276 ssl_dl_quicksand_begin, ssl_dl_quicksand_end, ssl_dl_sides_quicksand, 0xff, 0xff, 0xff, 0xff, 277 LAYER_OPAQUE }, 278 { MOVTEX_TREADMILL_BIG, TEX_YELLOW_TRI_TTC, 12, ttc_movtex_tris_big_surface_treadmill, 279 ttc_dl_surface_treadmill_begin, ttc_dl_surface_treadmill_end, ttc_dl_surface_treadmill, 0xff, 280 0xff, 0xff, 0xff, LAYER_OPAQUE }, 281 { MOVTEX_TREADMILL_SMALL, TEX_YELLOW_TRI_TTC, 12, ttc_movtex_tris_small_surface_treadmill, 282 ttc_dl_surface_treadmill_begin, ttc_dl_surface_treadmill_end, ttc_dl_surface_treadmill, 0xff, 283 0xff, 0xff, 0xff, LAYER_OPAQUE }, 284 { 0x00000000, 0x00000000, 0, NULL, NULL, NULL, NULL, 0x00, 0x00, 0x00, 0x00, 0x00000000 }, 285 }; 286 287 /** 288 * Treated identically to gMovtexColored. 289 */ 290 struct MovtexObject gMovtexColored2[] = { 291 { MOVTEX_SSL_SAND_PIT_OUTSIDE, TEX_QUICKSAND_SSL, 8, ssl_movtex_tris_quicksand_pit, 292 ssl_dl_quicksand_pit_begin, ssl_dl_quicksand_pit_end, ssl_dl_quicksand_pit, 0xff, 0xff, 0xff, 293 0xff, LAYER_OPAQUE }, 294 { MOVTEX_SSL_SAND_PIT_PYRAMID, TEX_PYRAMID_SAND_SSL, 8, ssl_movtex_tris_pyramid_quicksand_pit, 295 ssl_dl_pyramid_quicksand_pit_begin, ssl_dl_pyramid_quicksand_pit_end, ssl_dl_quicksand_pit, 0xff, 296 0xff, 0xff, 0xff, LAYER_OPAQUE }, 297 { 0x00000000, 0x00000000, 0, NULL, NULL, NULL, NULL, 0x00, 0x00, 0x00, 0x00, 0x00000000 }, 298 }; 299 300 /** 301 * Sets the initial water level in Wet-Dry World based on how high Mario 302 * jumped into the painting. 303 */ 304 Gfx *geo_wdw_set_initial_water_level(s32 callContext, UNUSED struct GraphNode *node, UNUSED Mat4 mtx) { 305 s32 i; 306 UNUSED u8 unused[] = { 1, 0, 4, 0, 7, 0, 10, 0 }; 307 s16 wdwWaterHeight; 308 309 // Why was this global variable needed when they could just check for GEO_CONTEXT_AREA_LOAD? 310 if (callContext != GEO_CONTEXT_RENDER) { 311 gWDWWaterLevelSet = FALSE; 312 } else if (callContext == GEO_CONTEXT_RENDER && gEnvironmentRegions != NULL 313 && !gWDWWaterLevelSet) { 314 if (gPaintingMarioYEntry <= 1382.4) { 315 wdwWaterHeight = 31; 316 } else if (gPaintingMarioYEntry >= 1600.0) { 317 wdwWaterHeight = 2816; 318 } else { 319 wdwWaterHeight = 1024; 320 } 321 for (i = 0; i < *gEnvironmentRegions; i++) { 322 gEnvironmentRegions[i * 6 + 6] = wdwWaterHeight; 323 } 324 gWDWWaterLevelSet = TRUE; 325 } 326 return NULL; 327 } 328 329 /** 330 * Update moving texture counters that determine when to update the coordinates. 331 * Textures update when gMovtexCounterPrev != gMovtexCounter. 332 * This ensures water / sand flow stops when the game pauses. 333 */ 334 Gfx *geo_movtex_pause_control(s32 callContext, UNUSED struct GraphNode *node, UNUSED Mat4 mtx) { 335 if (callContext != GEO_CONTEXT_RENDER) { 336 gMovtexCounterPrev = gAreaUpdateCounter - 1; 337 gMovtexCounter = gAreaUpdateCounter; 338 } else { 339 gMovtexCounterPrev = gMovtexCounter; 340 gMovtexCounter = gAreaUpdateCounter; 341 } 342 return NULL; 343 } 344 345 /** 346 * Make a vertex that's part of a quad with rotating texture. 347 * verts: array of RSP vertices 348 * n: index in 'verts' where the vertex is written 349 * x, y, z: position 350 * rot: base rotation of the texture 351 * rotOffset: gets added to base rotation 352 * scale: how often the texture repeats, 1 = no repeat 353 */ 354 void movtex_make_quad_vertex(Vtx *verts, s32 index, s16 x, s16 y, s16 z, s16 rot, s16 rotOffset, 355 f32 scale, u8 alpha) { 356 s16 s = 32.0 * (32.0 * scale - 1.0) * sins(rot + rotOffset); 357 s16 t = 32.0 * (32.0 * scale - 1.0) * coss(rot + rotOffset); 358 359 if (gMovtexVtxColor == MOVTEX_VTX_COLOR_YELLOW) { 360 make_vertex(verts, index, x, y, z, s, t, 255, 255, 0, alpha); 361 } else if (gMovtexVtxColor == MOVTEX_VTX_COLOR_RED) { 362 make_vertex(verts, index, x, y, z, s, t, 255, 0, 0, alpha); 363 } else { 364 make_vertex(verts, index, x, y, z, s, t, 255, 255, 255, alpha); 365 } 366 } 367 368 /** 369 * Represents a single flat quad with a rotating texture 370 * Stores x and z for 4 vertices, though it is often just a rectangle. 371 * Does not store the y-position, since that can be dynamic for water levels. 372 */ 373 struct MovtexQuad { 374 /// the current texture rotation in this quad 375 s16 rot; 376 /// gets added to rot every frame 377 s16 rotspeed; 378 /// the amount of times the texture repeats. 1 = no repeat. 379 s16 scale; 380 /// Coordinates of vertices 381 s16 x1; 382 s16 z1; 383 s16 x2; 384 s16 z2; 385 s16 x3; 386 s16 z3; 387 s16 x4; 388 s16 z4; 389 s16 rotDir; /// if 1, it rotates counter-clockwise 390 s16 alpha; /// opacity, 255 = fully opaque 391 s16 textureId; /// texture id 392 }; 393 394 /// Variable for a little optimization: only set the texture when it differs from the previous texture 395 s16 gMovetexLastTextureId; 396 397 /** 398 * Generates and returns a display list for a single MovtexQuad at height y. 399 */ 400 Gfx *movtex_gen_from_quad(s16 y, struct MovtexQuad *quad) { 401 s16 rot; 402 s16 rotspeed = quad->rotspeed; 403 s16 scale = quad->scale; 404 s16 x1 = quad->x1; 405 s16 z1 = quad->z1; 406 s16 x2 = quad->x2; 407 s16 z2 = quad->z2; 408 s16 x3 = quad->x3; 409 s16 z3 = quad->z3; 410 s16 x4 = quad->x4; 411 s16 z4 = quad->z4; 412 s16 rotDir = quad->rotDir; 413 s16 alpha = quad->alpha; 414 s16 textureId = quad->textureId; 415 Vtx *verts = alloc_display_list(4 * sizeof(*verts)); 416 Gfx *gfxHead; 417 Gfx *gfx; 418 419 if (textureId == gMovetexLastTextureId) { 420 gfxHead = alloc_display_list(3 * sizeof(*gfxHead)); 421 } else { 422 gfxHead = alloc_display_list(8 * sizeof(*gfxHead)); 423 } 424 425 if (gfxHead == NULL || verts == NULL) { 426 return NULL; 427 } 428 gfx = gfxHead; 429 if (gMovtexCounter != gMovtexCounterPrev) { 430 quad->rot += rotspeed; 431 } 432 rot = quad->rot; 433 if (rotDir == ROTATE_CLOCKWISE) { 434 movtex_make_quad_vertex(verts, 0, x1, y, z1, rot, 0, scale, alpha); 435 movtex_make_quad_vertex(verts, 1, x2, y, z2, rot, 16384, scale, alpha); 436 movtex_make_quad_vertex(verts, 2, x3, y, z3, rot, -32768, scale, alpha); 437 movtex_make_quad_vertex(verts, 3, x4, y, z4, rot, -16384, scale, alpha); 438 } else { // ROTATE_COUNTER_CLOCKWISE 439 movtex_make_quad_vertex(verts, 0, x1, y, z1, rot, 0, scale, alpha); 440 movtex_make_quad_vertex(verts, 1, x2, y, z2, rot, -16384, scale, alpha); 441 movtex_make_quad_vertex(verts, 2, x3, y, z3, rot, -32768, scale, alpha); 442 movtex_make_quad_vertex(verts, 3, x4, y, z4, rot, 16384, scale, alpha); 443 } 444 445 // Only add commands to change the texture when necessary 446 if (textureId != gMovetexLastTextureId) { 447 switch (textureId) { 448 case TEXTURE_MIST: // an ia16 texture 449 gLoadBlockTexture(gfx++, 32, 32, G_IM_FMT_IA, gMovtexIdToTexture[textureId]); 450 break; 451 default: // any rgba16 texture 452 gLoadBlockTexture(gfx++, 32, 32, G_IM_FMT_RGBA, gMovtexIdToTexture[textureId]); 453 break; 454 } 455 gMovetexLastTextureId = textureId; 456 } 457 gSPVertex(gfx++, VIRTUAL_TO_PHYSICAL2(verts), 4, 0); 458 gSPDisplayList(gfx++, dl_draw_quad_verts_0123); 459 gSPEndDisplayList(gfx); 460 return gfxHead; 461 } 462 463 /** 464 * Generate a display list drawing an array of MoxtexQuad at height 'y'. 465 * y: y position of the quads 466 * quadArrSegmented: a segmented address to an array of s16. The first number 467 * is the number of entries, followed by that number of MovtexQuad structs. 468 */ 469 Gfx *movtex_gen_from_quad_array(s16 y, void *quadArrSegmented) { 470 s16 *quadArr = segmented_to_virtual(quadArrSegmented); 471 s16 numLists = quadArr[0]; 472 Gfx *gfxHead = alloc_display_list((numLists + 1) * sizeof(*gfxHead)); 473 Gfx *gfx = gfxHead; 474 Gfx *subList; 475 s32 i; 476 477 if (gfxHead == NULL) { 478 return NULL; 479 } 480 for (i = 0; i < numLists; i++) { 481 // quadArr is an array of s16, so sizeof(MovtexQuad) gets divided by 2 482 subList = movtex_gen_from_quad( 483 y, (struct MovtexQuad *) (&quadArr[i * (sizeof(struct MovtexQuad) / 2) + 1])); 484 if (subList != NULL) { 485 gSPDisplayList(gfx++, VIRTUAL_TO_PHYSICAL(subList)); 486 } 487 } 488 gSPEndDisplayList(gfx); 489 return gfxHead; 490 } 491 492 /** 493 * Generate the display list for a list of quads by searching through a collection 494 * for a given id. 495 * id: id of quad array to generate a list for 496 * y: height at which the quads are drawn 497 * movetexQuadsSegmented: segmented address to the MovtexQuadCollection array 498 * that will be searched. 499 */ 500 Gfx *movtex_gen_quads_id(s16 id, s16 y, void *movetexQuadsSegmented) { 501 struct MovtexQuadCollection *collection = segmented_to_virtual(movetexQuadsSegmented); 502 s32 i = 0; 503 504 while (collection[i].id != -1) { 505 if (collection[i].id == id) { 506 return movtex_gen_from_quad_array(y, collection[i].quadArraySegmented); 507 } 508 i++; 509 } 510 return NULL; 511 } 512 513 extern u8 bbh_movtex_merry_go_round_water_entrance[]; 514 extern u8 bbh_movtex_merry_go_round_water_side[]; 515 extern u8 ccm_movtex_penguin_puddle_water[]; 516 extern u8 inside_castle_movtex_green_room_water[]; 517 extern u8 inside_castle_movtex_moat_water[]; 518 extern u8 hmc_movtex_dorrie_pool_water[]; 519 extern u8 hmc_movtex_toxic_maze_mist[]; 520 extern u8 ssl_movtex_puddle_water[]; 521 extern u8 ssl_movtex_toxbox_quicksand_mist[]; 522 extern u8 sl_movtex_water[]; 523 extern u8 wdw_movtex_area1_water[]; 524 extern u8 wdw_movtex_area2_water[]; 525 extern u8 jrb_movtex_water[]; 526 extern u8 jrb_movtex_initial_mist[]; 527 extern u8 jrb_movtex_sunken_ship_water[]; 528 extern u8 thi_movtex_area1_water[]; 529 extern u8 thi_movtex_area2_water[]; 530 extern u8 castle_grounds_movtex_water[]; 531 extern u8 lll_movtex_volcano_floor_lava[]; 532 extern u8 ddd_movtex_area1_water[]; 533 extern u8 ddd_movtex_area2_water[]; 534 extern u8 wf_movtex_water[]; 535 extern u8 castle_courtyard_movtex_star_statue_water[]; 536 extern u8 ttm_movtex_puddle[]; 537 538 /** 539 * Find the quadCollection for a given quad collection id. 540 */ 541 void *get_quad_collection_from_id(u32 id) { 542 switch (id) { 543 case BBH_MOVTEX_MERRY_GO_ROUND_WATER_ENTRANCE: 544 return bbh_movtex_merry_go_round_water_entrance; 545 case BBH_MOVTEX_MERRY_GO_ROUND_WATER_SIDE: 546 return bbh_movtex_merry_go_round_water_side; 547 case CCM_MOVTEX_PENGUIN_PUDDLE_WATER: 548 return ccm_movtex_penguin_puddle_water; 549 case INSIDE_CASTLE_MOVTEX_GREEN_ROOM_WATER: 550 return inside_castle_movtex_green_room_water; 551 case INSIDE_CASTLE_MOVTEX_MOAT_WATER: 552 return inside_castle_movtex_moat_water; 553 case HMC_MOVTEX_DORRIE_POOL_WATER: 554 return hmc_movtex_dorrie_pool_water; 555 case HMC_MOVTEX_TOXIC_MAZE_MIST: 556 return hmc_movtex_toxic_maze_mist; 557 case SSL_MOVTEX_PUDDLE_WATER: 558 return ssl_movtex_puddle_water; 559 case SSL_MOVTEX_TOXBOX_QUICKSAND_MIST: 560 return ssl_movtex_toxbox_quicksand_mist; 561 case SL_MOVTEX_WATER: 562 return sl_movtex_water; 563 case WDW_MOVTEX_AREA1_WATER: 564 return wdw_movtex_area1_water; 565 case WDW_MOVTEX_AREA2_WATER: 566 return wdw_movtex_area2_water; 567 case JRB_MOVTEX_WATER: 568 return jrb_movtex_water; 569 case JRB_MOVTEX_INITIAL_MIST: 570 return jrb_movtex_initial_mist; 571 case JRB_MOVTEX_SUNKEN_SHIP_WATER: 572 return jrb_movtex_sunken_ship_water; 573 case THI_MOVTEX_AREA1_WATER: 574 return thi_movtex_area1_water; 575 case THI_MOVTEX_AREA2_WATER: 576 return thi_movtex_area2_water; 577 case CASTLE_GROUNDS_MOVTEX_WATER: 578 return castle_grounds_movtex_water; 579 case LLL_MOVTEX_VOLCANO_FLOOR_LAVA: 580 return lll_movtex_volcano_floor_lava; 581 case DDD_MOVTEX_AREA1_WATER: 582 return ddd_movtex_area1_water; 583 case DDD_MOVTEX_AREA2_WATER: 584 return ddd_movtex_area2_water; 585 case WF_MOVTEX_WATER: 586 return wf_movtex_water; 587 case CASTLE_COURTYARD_MOVTEX_STAR_STATUE_WATER: 588 return castle_courtyard_movtex_star_statue_water; 589 case TTM_MOVTEX_PUDDLE: 590 return ttm_movtex_puddle; 591 default: 592 return NULL; 593 } 594 } 595 596 /** 597 * Write to 'gfx' a command to set the current texture format for the given 598 * quadCollection. 599 */ 600 void movtex_change_texture_format(u32 quadCollectionId, Gfx **gfx) { 601 switch (quadCollectionId) { 602 case HMC_MOVTEX_TOXIC_MAZE_MIST: 603 gSPDisplayList((*gfx)++, dl_waterbox_ia16_begin); 604 break; 605 case SSL_MOVTEX_TOXBOX_QUICKSAND_MIST: 606 gSPDisplayList((*gfx)++, dl_waterbox_ia16_begin); 607 break; 608 case JRB_MOVTEX_INITIAL_MIST: 609 gSPDisplayList((*gfx)++, dl_waterbox_ia16_begin); 610 break; 611 default: 612 gSPDisplayList((*gfx)++, dl_waterbox_rgba16_begin); 613 break; 614 } 615 } 616 617 /** 618 * Geo script responsible for drawing quads with a moving texture at the height 619 * of the corresponding water region. The node's parameter determines which quad 620 * collection is drawn, see moving_texture.h. 621 */ 622 Gfx *geo_movtex_draw_water_regions(s32 callContext, struct GraphNode *node, UNUSED Mat4 mtx) { 623 Gfx *gfxHead = NULL; 624 Gfx *gfx = NULL; 625 Gfx *subList; 626 void *quadCollection; 627 struct GraphNodeGenerated *asGenerated; 628 s16 numWaterBoxes; 629 s16 waterId; 630 s16 waterY; 631 s32 i; 632 633 if (callContext == GEO_CONTEXT_RENDER) { 634 gMovtexVtxColor = MOVTEX_VTX_COLOR_DEFAULT; 635 if (gEnvironmentRegions == NULL) { 636 return NULL; 637 } 638 numWaterBoxes = gEnvironmentRegions[0]; 639 gfxHead = alloc_display_list((numWaterBoxes + 3) * sizeof(*gfxHead)); 640 if (gfxHead == NULL) { 641 return NULL; 642 } else { 643 gfx = gfxHead; 644 } 645 asGenerated = (struct GraphNodeGenerated *) node; 646 if (asGenerated->parameter == JRB_MOVTEX_INITIAL_MIST) { 647 if (gLakituState.goalPos[1] < 1024.0) { // if camera under water 648 return NULL; 649 } 650 if (save_file_get_star_flags(gCurrSaveFileNum - 1, COURSE_NUM_TO_INDEX(COURSE_JRB)) 651 & (1 << 0)) { // the "Plunder in the Sunken Ship" star in JRB is collected 652 return NULL; 653 } 654 } else if (asGenerated->parameter == HMC_MOVTEX_TOXIC_MAZE_MIST) { 655 gMovtexVtxColor = MOVTEX_VTX_COLOR_YELLOW; 656 } else if (asGenerated->parameter == SSL_MOVTEX_TOXBOX_QUICKSAND_MIST) { 657 gMovtexVtxColor = MOVTEX_VTX_COLOR_RED; 658 } 659 quadCollection = get_quad_collection_from_id(asGenerated->parameter); 660 if (quadCollection == NULL) { 661 return NULL; 662 } 663 664 asGenerated->fnNode.node.flags = 665 (asGenerated->fnNode.node.flags & 0xFF) | (LAYER_TRANSPARENT_INTER << 8); 666 667 movtex_change_texture_format(asGenerated->parameter, &gfx); 668 gMovetexLastTextureId = -1; 669 for (i = 0; i < numWaterBoxes; i++) { 670 waterId = gEnvironmentRegions[i * 6 + 1]; 671 waterY = gEnvironmentRegions[i * 6 + 6]; 672 subList = movtex_gen_quads_id(waterId, waterY, quadCollection); 673 if (subList != NULL) { 674 gSPDisplayList(gfx++, VIRTUAL_TO_PHYSICAL(subList)); 675 } 676 } 677 gSPDisplayList(gfx++, dl_waterbox_end); 678 gSPEndDisplayList(gfx); 679 } 680 return gfxHead; 681 } 682 683 /** 684 * Updates a movtex mesh by adding the movtex's speed to the horizontal or 685 * vertical texture coordinates depending on 'attr'. 686 * movtexVerts: vertices to update 687 * attr: which attribute to change 688 */ 689 void update_moving_texture_offset(s16 *movtexVerts, s32 attr) { 690 s16 movSpeed = movtexVerts[MOVTEX_ATTR_SPEED]; 691 s16 *curOffset = movtexVerts + attr; 692 693 if (gMovtexCounter != gMovtexCounterPrev) { 694 *curOffset += movSpeed; 695 // note that texture coordinates are 6.10 fixed point, so this does modulo 1 696 if (*curOffset >= 1024) { 697 *curOffset -= 1024; 698 } 699 if (*curOffset <= -1024) { 700 *curOffset += 1024; 701 } 702 } 703 } 704 705 /** 706 * Make the first vertex of a moving texture with index 0. 707 * This vertex is the base of all vertices with index > 0, which use this 708 * vertex's coordinates as base on which to apply offset. 709 * The first vertex has offset 0 by definition, simplifying the calculations a bit. 710 */ 711 void movtex_write_vertex_first(Vtx *vtx, s16 *movtexVerts, struct MovtexObject *c, s8 attrLayout) { 712 s16 x = movtexVerts[MOVTEX_ATTR_X]; 713 s16 y = movtexVerts[MOVTEX_ATTR_Y]; 714 s16 z = movtexVerts[MOVTEX_ATTR_Z]; 715 u8 alpha = c->a; 716 u8 r1; 717 u8 g1; 718 u8 b1; 719 s8 r2; 720 s8 g2; 721 s8 b2; 722 s16 s; 723 s16 t; 724 725 switch (attrLayout) { 726 case MOVTEX_LAYOUT_NOCOLOR: 727 r1 = c->r; 728 g1 = c->g; 729 b1 = c->b; 730 s = movtexVerts[MOVTEX_ATTR_NOCOLOR_S]; 731 t = movtexVerts[MOVTEX_ATTR_NOCOLOR_T]; 732 make_vertex(vtx, 0, x, y, z, s, t, r1, g1, b1, alpha); 733 break; 734 case MOVTEX_LAYOUT_COLORED: 735 r2 = movtexVerts[MOVTEX_ATTR_COLORED_R]; 736 g2 = movtexVerts[MOVTEX_ATTR_COLORED_G]; 737 b2 = movtexVerts[MOVTEX_ATTR_COLORED_B]; 738 s = movtexVerts[MOVTEX_ATTR_COLORED_S]; 739 t = movtexVerts[MOVTEX_ATTR_COLORED_T]; 740 make_vertex(vtx, 0, x, y, z, s, t, r2, g2, b2, alpha); 741 break; 742 } 743 } 744 745 /** 746 * Make a vertex with index > 0. The vertex with index 0 is made in 747 * movtex_write_vertex_first and subsequent vertices use vertex 0 as a base 748 * for their texture coordinates. 749 */ 750 void movtex_write_vertex_index(Vtx *verts, s32 index, s16 *movtexVerts, struct MovtexObject *d, 751 s8 attrLayout) { 752 u8 alpha = d->a; 753 s16 x; 754 s16 y; 755 s16 z; 756 s16 baseS; 757 s16 baseT; 758 s16 s; 759 s16 t; 760 s16 offS; 761 s16 offT; 762 u8 r1; 763 u8 g1; 764 u8 b1; 765 s8 r2; 766 s8 g2; 767 s8 b2; 768 769 switch (attrLayout) { 770 case MOVTEX_LAYOUT_NOCOLOR: 771 x = movtexVerts[index * 5 + MOVTEX_ATTR_X]; 772 y = movtexVerts[index * 5 + MOVTEX_ATTR_Y]; 773 z = movtexVerts[index * 5 + MOVTEX_ATTR_Z]; 774 baseS = movtexVerts[MOVTEX_ATTR_NOCOLOR_S]; 775 baseT = movtexVerts[MOVTEX_ATTR_NOCOLOR_T]; 776 offS = movtexVerts[index * 5 + MOVTEX_ATTR_NOCOLOR_S]; 777 offT = movtexVerts[index * 5 + MOVTEX_ATTR_NOCOLOR_T]; 778 s = baseS + ((offS * 32) * 32U); 779 t = baseT + ((offT * 32) * 32U); 780 r1 = d->r; 781 g1 = d->g; 782 b1 = d->b; 783 make_vertex(verts, index, x, y, z, s, t, r1, g1, b1, alpha); 784 break; 785 case MOVTEX_LAYOUT_COLORED: 786 x = movtexVerts[index * 8 + MOVTEX_ATTR_X]; 787 y = movtexVerts[index * 8 + MOVTEX_ATTR_Y]; 788 z = movtexVerts[index * 8 + MOVTEX_ATTR_Z]; 789 baseS = movtexVerts[7]; 790 baseT = movtexVerts[8]; 791 offS = movtexVerts[index * 8 + 7]; 792 offT = movtexVerts[index * 8 + 8]; 793 s = baseS + ((offS * 32) * 32U); 794 t = baseT + ((offT * 32) * 32U); 795 r2 = movtexVerts[index * 8 + MOVTEX_ATTR_COLORED_R]; 796 g2 = movtexVerts[index * 8 + MOVTEX_ATTR_COLORED_G]; 797 b2 = movtexVerts[index * 8 + MOVTEX_ATTR_COLORED_B]; 798 make_vertex(verts, index, x, y, z, s, t, r2, g2, b2, alpha); 799 break; 800 } 801 } 802 803 /** 804 * Generate a displaylist for a MovtexObject. 805 * 'attrLayout' is one of MOVTEX_LAYOUT_NOCOLOR and MOVTEX_LAYOUT_COLORED. 806 */ 807 Gfx *movtex_gen_list(s16 *movtexVerts, struct MovtexObject *movtexList, s8 attrLayout) { 808 Vtx *verts = alloc_display_list(movtexList->vtx_count * sizeof(*verts)); 809 Gfx *gfxHead = alloc_display_list(11 * sizeof(*gfxHead)); 810 Gfx *gfx = gfxHead; 811 s32 i; 812 813 if (verts == NULL || gfxHead == NULL) { 814 return NULL; 815 } 816 817 movtex_write_vertex_first(verts, movtexVerts, movtexList, attrLayout); 818 for (i = 1; i < movtexList->vtx_count; i++) { 819 movtex_write_vertex_index(verts, i, movtexVerts, movtexList, attrLayout); 820 } 821 822 gSPDisplayList(gfx++, movtexList->beginDl); 823 gLoadBlockTexture(gfx++, 32, 32, G_IM_FMT_RGBA, gMovtexIdToTexture[movtexList->textureId]); 824 gSPVertex(gfx++, VIRTUAL_TO_PHYSICAL2(verts), movtexList->vtx_count, 0); 825 gSPDisplayList(gfx++, movtexList->triDl); 826 gSPDisplayList(gfx++, movtexList->endDl); 827 gSPEndDisplayList(gfx); 828 return gfxHead; 829 } 830 831 /** 832 * Function for a geo node that draws a MovtexObject in the gMovtexNonColored list. 833 */ 834 Gfx *geo_movtex_draw_nocolor(s32 callContext, struct GraphNode *node, UNUSED Mat4 mtx) { 835 s32 i; 836 s16 *movtexVerts; 837 struct GraphNodeGenerated *asGenerated; 838 Gfx *gfx = NULL; 839 840 if (callContext == GEO_CONTEXT_RENDER) { 841 i = 0; 842 asGenerated = (struct GraphNodeGenerated *) node; 843 while (gMovtexNonColored[i].movtexVerts != 0) { 844 if (gMovtexNonColored[i].geoId == asGenerated->parameter) { 845 asGenerated->fnNode.node.flags = 846 (asGenerated->fnNode.node.flags & 0xFF) | (gMovtexNonColored[i].layer << 8); 847 movtexVerts = segmented_to_virtual(gMovtexNonColored[i].movtexVerts); 848 update_moving_texture_offset(movtexVerts, MOVTEX_ATTR_NOCOLOR_S); 849 gfx = movtex_gen_list(movtexVerts, &gMovtexNonColored[i], 850 MOVTEX_LAYOUT_NOCOLOR); // no perVertex colors 851 break; 852 } 853 i++; 854 } 855 } 856 return gfx; 857 } 858 859 /** 860 * Function for a geo node that draws a MovtexObject in the gMovtexColored list. 861 */ 862 Gfx *geo_movtex_draw_colored(s32 callContext, struct GraphNode *node, UNUSED Mat4 mtx) { 863 s32 i; 864 s16 *movtexVerts; 865 struct GraphNodeGenerated *asGenerated; 866 Gfx *gfx = NULL; 867 868 if (callContext == GEO_CONTEXT_RENDER) { 869 i = 0; 870 asGenerated = (struct GraphNodeGenerated *) node; 871 while (gMovtexColored[i].movtexVerts != 0) { 872 if (gMovtexColored[i].geoId == asGenerated->parameter) { 873 asGenerated->fnNode.node.flags = 874 (asGenerated->fnNode.node.flags & 0xFF) | (gMovtexColored[i].layer << 8); 875 movtexVerts = segmented_to_virtual(gMovtexColored[i].movtexVerts); 876 update_moving_texture_offset(movtexVerts, MOVTEX_ATTR_COLORED_S); 877 gfx = movtex_gen_list(movtexVerts, &gMovtexColored[i], MOVTEX_LAYOUT_COLORED); 878 break; 879 } 880 i++; 881 } 882 } 883 return gfx; 884 } 885 886 /** 887 * Function for a geo node that draws a MovtexObject in the gMovtexColored list, 888 * but it doesn't call update_moving_texture_offset since that happens in 889 * geo_movtex_update_horizontal. This is for when a MovtexObject has multiple 890 * instances (like TTC treadmills) so you don't want the animation speed to 891 * increase the more instances there are. 892 */ 893 Gfx *geo_movtex_draw_colored_no_update(s32 callContext, struct GraphNode *node, UNUSED Mat4 mtx) { 894 s32 i; 895 s16 *movtexVerts; 896 struct GraphNodeGenerated *asGenerated; 897 Gfx *gfx = NULL; 898 899 if (callContext == GEO_CONTEXT_RENDER) { 900 i = 0; 901 asGenerated = (struct GraphNodeGenerated *) node; 902 while (gMovtexColored[i].movtexVerts != 0) { 903 if (gMovtexColored[i].geoId == asGenerated->parameter) { 904 asGenerated->fnNode.node.flags = 905 (asGenerated->fnNode.node.flags & 0xFF) | (gMovtexColored[i].layer << 8); 906 movtexVerts = segmented_to_virtual(gMovtexColored[i].movtexVerts); 907 gfx = movtex_gen_list(movtexVerts, &gMovtexColored[i], MOVTEX_LAYOUT_COLORED); 908 break; 909 } 910 i++; 911 } 912 } 913 return gfx; 914 } 915 916 /** 917 * Exact copy of geo_movtex_draw_colored_no_update, but now using the gMovtexColored2 array. 918 * Used for the sand pits in SSL, both outside and inside the pyramid. 919 */ 920 Gfx *geo_movtex_draw_colored_2_no_update(s32 callContext, struct GraphNode *node, UNUSED Mat4 mtx) { 921 s32 i; 922 s16 *movtexVerts; 923 struct GraphNodeGenerated *asGenerated; 924 Gfx *gfx = NULL; 925 926 if (callContext == GEO_CONTEXT_RENDER) { 927 i = 0; 928 asGenerated = (struct GraphNodeGenerated *) node; 929 while (gMovtexColored2[i].movtexVerts != 0) { 930 if (gMovtexColored2[i].geoId == asGenerated->parameter) { 931 asGenerated->fnNode.node.flags = 932 (asGenerated->fnNode.node.flags & 0xFF) | (gMovtexColored2[i].layer << 8); 933 movtexVerts = segmented_to_virtual(gMovtexColored2[i].movtexVerts); 934 gfx = movtex_gen_list(movtexVerts, &gMovtexColored2[i], MOVTEX_LAYOUT_COLORED); 935 break; 936 } 937 i++; 938 } 939 } 940 return gfx; 941 } 942 943 /** 944 * Make textures move horizontally by simply adding a number to the 's' texture coordinate. 945 * Used for: 946 * - treadmills in Tick Tock Clock 947 * - sand pits outside and inside the pyramid in Shifting Sand 948 * Note that the drawing for these happen in different nodes with functions 949 * geo_movtex_draw_colored_no_update and geo_movtex_draw_colored_2_no_update. 950 * Usually the updating happens in the same function that draws it, but in 951 * these cases the same model has multiple instances, and you don't want the 952 * model to update multiple times. 953 * Note that the final TTC only has one big treadmill though. 954 */ 955 Gfx *geo_movtex_update_horizontal(s32 callContext, struct GraphNode *node, UNUSED Mat4 mtx) { 956 void *movtexVerts; 957 958 if (callContext == GEO_CONTEXT_RENDER) { 959 struct GraphNodeGenerated *asGenerated = (struct GraphNodeGenerated *) node; 960 961 switch (asGenerated->parameter) { 962 case MOVTEX_SSL_SAND_PIT_OUTSIDE: 963 movtexVerts = segmented_to_virtual(ssl_movtex_tris_quicksand_pit); 964 break; 965 case MOVTEX_SSL_SAND_PIT_PYRAMID: 966 movtexVerts = segmented_to_virtual(ssl_movtex_tris_pyramid_quicksand_pit); 967 break; 968 case MOVTEX_TREADMILL_BIG: 969 movtexVerts = segmented_to_virtual(ttc_movtex_tris_big_surface_treadmill); 970 break; 971 case MOVTEX_TREADMILL_SMALL: 972 movtexVerts = segmented_to_virtual(ttc_movtex_tris_small_surface_treadmill); 973 break; 974 } 975 update_moving_texture_offset(movtexVerts, MOVTEX_ATTR_COLORED_S); 976 } 977 return NULL; 978 }