2019-08-25 04:46:40 +00:00
|
|
|
#include <ultra64.h>
|
|
|
|
|
|
|
|
#include "sm64.h"
|
|
|
|
#include "moving_texture.h"
|
|
|
|
#include "area.h"
|
|
|
|
#include "camera.h"
|
|
|
|
#include "rendering_graph_node.h"
|
|
|
|
#include "engine/math_util.h"
|
|
|
|
#include "memory.h"
|
|
|
|
#include "save_file.h"
|
|
|
|
#include "segment2.h"
|
|
|
|
#include "engine/surface_collision.h"
|
|
|
|
#include "geo_misc.h"
|
|
|
|
#include "rendering_graph_node.h"
|
2020-02-03 05:51:26 +00:00
|
|
|
#include "object_list_processor.h"
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* This file contains functions for generating display lists with moving textures
|
|
|
|
* (abbreviated movtex). This is used for water, sand, haze, mist and treadmills.
|
|
|
|
* Each mesh using this system has the animated vertices stored as an array of shorts.
|
|
|
|
* The first entry is the texture movement speed. After that the vertices are stored
|
|
|
|
* in one of two layouts: one without per-vertex color attributes and one with.
|
|
|
|
* [speed, v0(x,y,z, s,t) , v1(x,y,z, s,t) , ...]
|
|
|
|
* [speed, v0(x,y,z, r,g,b s,t), v1(x,y,z, r,g,b s,t), ...]
|
|
|
|
* x, y, z = vertex position as integers
|
|
|
|
* s, t = texture coordinates as 6.10 fixed point number. That means coordinates in
|
|
|
|
* range [0, 1024] are a unique part of the image, after that it repeats the image.
|
2019-08-25 04:46:40 +00:00
|
|
|
*
|
2019-09-01 19:50:50 +00:00
|
|
|
* The first vertex 'v0' is special because all subsequent vertices inherit its
|
|
|
|
* texture offset. So to animate e.g. a treadmill, the speed component arr[0] is
|
|
|
|
* simply added to the s component arr[7] every frame and the texture scrolls
|
|
|
|
* horizontally over the entire mesh without changing the rest of the array.
|
|
|
|
* Note that while the system allows many kinds of vertex animations, in
|
|
|
|
* practice the only animation used is horizontally scrolling textures.
|
2019-08-25 04:46:40 +00:00
|
|
|
*
|
2019-09-01 19:50:50 +00:00
|
|
|
* After updating the base mesh, the vertices are converted to the format the RSP
|
|
|
|
* understands and a display list is generated. The RSP can buffer 16 vertices at
|
|
|
|
* a time, and this code assumes everything fits in one buffer, so every moving
|
|
|
|
* texture mesh must have at most 16 vertices. As a result some meshes are split
|
|
|
|
* up into multiple parts, like the sand pathway inside the pyramid which has 3
|
|
|
|
* parts. The water stream in the Cavern of the Metal Cap fits in one mesh.
|
2019-08-25 04:46:40 +00:00
|
|
|
*
|
2019-09-01 19:50:50 +00:00
|
|
|
* Apart from this general system, there is also a simpler system for flat
|
|
|
|
* quads with a rotating texture. This is often used for water, but also
|
|
|
|
* for mist, toxic haze and lava inside the volcano. One quad is described
|
|
|
|
* by the struct MovtexQuad, and multiple MovtexQuads form a MovtexQuadCollection.
|
|
|
|
* A geo node has an id that corresponds to the id of a certain MovtexQuadCollection,
|
|
|
|
* which will then be matched with the id of entries in gEnvironmentRegions to get the
|
|
|
|
* y-position. The x and z coordinates are stored in the MovtexQuads themself,
|
|
|
|
* so the water rectangle is separate from the actually drawn rectangle.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
// First entry in array is texture movement speed for both layouts
|
|
|
|
#define MOVTEX_ATTR_SPEED 0
|
|
|
|
|
|
|
|
// Different layouts for vertices
|
|
|
|
#define MOVTEX_LAYOUT_NOCOLOR 0
|
|
|
|
#define MOVTEX_LAYOUT_COLORED 1
|
|
|
|
|
|
|
|
// Attributes for movtex vertices
|
|
|
|
#define MOVTEX_ATTR_X 1
|
|
|
|
#define MOVTEX_ATTR_Y 2
|
|
|
|
#define MOVTEX_ATTR_Z 3
|
|
|
|
|
|
|
|
// For MOVTEX_LAYOUT_NOCOLOR only
|
|
|
|
#define MOVTEX_ATTR_NOCOLOR_S 4
|
|
|
|
#define MOVTEX_ATTR_NOCOLOR_T 5
|
|
|
|
|
|
|
|
// For MOVTEX_LAYOUT_COLORED only
|
|
|
|
#define MOVTEX_ATTR_COLORED_R 4
|
|
|
|
#define MOVTEX_ATTR_COLORED_G 5
|
|
|
|
#define MOVTEX_ATTR_COLORED_B 6
|
|
|
|
#define MOVTEX_ATTR_COLORED_S 7
|
|
|
|
#define MOVTEX_ATTR_COLORED_T 8
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* An object containing all info for a mesh with moving textures.
|
|
|
|
* Contains the vertices that are animated, but also the display list which
|
|
|
|
* determines the connectivity, as well as the texture, texture blend color
|
|
|
|
* and drawing layer.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
struct MovtexObject {
|
|
|
|
/// number that geo nodes have as parameter to refer to this mesh
|
|
|
|
u32 geoId;
|
|
|
|
/// which texture to use for this mesh, index into gMovtexIdToTexture
|
|
|
|
s32 textureId;
|
|
|
|
/// amount of moving vertices
|
|
|
|
s32 vtx_count;
|
|
|
|
/// segmented address to movtex mesh with vertices
|
|
|
|
void *movtexVerts;
|
|
|
|
/// display list inserted before moving triangles
|
|
|
|
Gfx *beginDl;
|
|
|
|
/// display list inserted after moving triangles
|
|
|
|
Gfx *endDl;
|
|
|
|
/// display list with the actual moving texture triangles.
|
|
|
|
/// Assumes the animated vertices are buffered and correct texture is set
|
|
|
|
Gfx *triDl;
|
|
|
|
// if the list does not have per-vertex colors, all vertices have these colors
|
|
|
|
u8 r; /// red
|
|
|
|
u8 g; /// green
|
|
|
|
u8 b; /// blue
|
|
|
|
u8 a; /// alpha
|
|
|
|
s32 layer; /// the drawing layer for this mesh
|
|
|
|
};
|
|
|
|
|
|
|
|
/// Counters to make textures move iff the game is not paused.
|
|
|
|
s16 gMovtexCounter = 1;
|
|
|
|
s16 gMovtexCounterPrev = 0;
|
|
|
|
|
|
|
|
// Vertex colors for rectangles. Used to give mist a tint
|
|
|
|
#define MOVTEX_VTX_COLOR_DEFAULT 0 // no tint (white vertex colors)
|
|
|
|
#define MOVTEX_VTX_COLOR_YELLOW 1 // used for Hazy Maze Cave toxic haze
|
|
|
|
#define MOVTEX_VTX_COLOR_RED 2 // used for Shifting Sand Land around the Tox box maze
|
|
|
|
|
|
|
|
s8 gMovtexVtxColor = MOVTEX_VTX_COLOR_DEFAULT;
|
|
|
|
|
|
|
|
/// The height at which Mario entered the last painting. Used for Wet-Dry World only.
|
|
|
|
float gPaintingMarioYEntry = 0.0f;
|
|
|
|
|
|
|
|
/// Variable to ensure the initial Wet-Dry World water level is set only once
|
|
|
|
s32 gWdwWaterLevelSet = FALSE;
|
|
|
|
|
|
|
|
extern u8 ssl_quicksand[];
|
|
|
|
extern u8 ssl_pyramid_sand[];
|
|
|
|
extern u8 ttc_yellow_triangle[];
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* An array for converting a movtex texture id to a pointer that can
|
|
|
|
* be passed to gDPSetTextureImage.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
u8 *gMovtexIdToTexture[] = { texture_waterbox_water, texture_waterbox_mist,
|
|
|
|
texture_waterbox_jrb_water, texture_waterbox_unknown_water,
|
|
|
|
texture_waterbox_lava, ssl_quicksand,
|
|
|
|
ssl_pyramid_sand, ttc_yellow_triangle };
|
|
|
|
|
|
|
|
extern Gfx castle_grounds_dl_waterfall[];
|
|
|
|
extern s16 castle_grounds_movtex_tris_waterfall[];
|
|
|
|
extern s16 ssl_movtex_tris_pyramid_sand_pathway_front[];
|
|
|
|
extern Gfx ssl_dl_pyramid_sand_pathway_begin[];
|
|
|
|
extern Gfx ssl_dl_pyramid_sand_pathway_end[];
|
|
|
|
extern Gfx ssl_dl_pyramid_sand_pathway_front_end[];
|
|
|
|
extern s16 ssl_movtex_tris_pyramid_sand_pathway_floor[];
|
|
|
|
extern Gfx ssl_dl_pyramid_sand_pathway_floor_begin[];
|
|
|
|
extern Gfx ssl_dl_pyramid_sand_pathway_floor_end[];
|
|
|
|
extern s16 ssl_movtex_tris_pyramid_sand_pathway_side[];
|
|
|
|
extern Gfx ssl_dl_pyramid_sand_pathway_side_end[];
|
|
|
|
extern s16 bitfs_movtex_tris_lava_first_section[];
|
|
|
|
extern Gfx bitfs_dl_lava_sections[];
|
|
|
|
extern s16 bitfs_movtex_tris_lava_second_section[];
|
|
|
|
extern s16 bitfs_movtex_tris_lava_floor[];
|
|
|
|
extern Gfx bitfs_dl_lava_floor[];
|
|
|
|
extern s16 lll_movtex_tris_lava_floor[];
|
|
|
|
extern Gfx lll_dl_lava_floor[];
|
|
|
|
extern s16 lll_movtex_tris_lavafall_volcano[];
|
|
|
|
extern Gfx lll_dl_lavafall_volcano[];
|
|
|
|
extern s16 cotmc_movtex_tris_water[];
|
|
|
|
extern Gfx cotmc_dl_water_begin[];
|
|
|
|
extern Gfx cotmc_dl_water_end[];
|
|
|
|
extern Gfx cotmc_dl_water[];
|
|
|
|
extern s16 ttm_movtex_tris_begin_waterfall[];
|
|
|
|
extern Gfx ttm_dl_waterfall[];
|
|
|
|
extern s16 ttm_movtex_tris_end_waterfall[];
|
|
|
|
extern s16 ttm_movtex_tris_begin_puddle_waterfall[];
|
|
|
|
extern Gfx ttm_dl_bottom_waterfall[];
|
|
|
|
extern s16 ttm_movtex_tris_end_puddle_waterfall[];
|
|
|
|
extern s16 ttm_movtex_tris_puddle_waterfall[];
|
|
|
|
extern Gfx ttm_dl_puddle_waterfall[];
|
|
|
|
extern s16 ssl_movtex_tris_pyramid_quicksand[];
|
|
|
|
extern Gfx ssl_dl_quicksand_begin[];
|
|
|
|
extern Gfx ssl_dl_quicksand_end[];
|
|
|
|
extern Gfx ssl_dl_pyramid_quicksand[];
|
|
|
|
extern s16 ssl_movtex_tris_pyramid_corners_quicksand[];
|
|
|
|
extern Gfx ssl_dl_pyramid_corners_quicksand[];
|
|
|
|
extern s16 ssl_movtex_tris_sides_quicksand[];
|
|
|
|
extern Gfx ssl_dl_sides_quicksand[];
|
|
|
|
extern s16 ttc_movtex_tris_big_surface_treadmill[];
|
|
|
|
extern Gfx ttc_dl_surface_treadmill_begin[];
|
|
|
|
extern Gfx ttc_dl_surface_treadmill_end[];
|
|
|
|
extern Gfx ttc_dl_surface_treadmill[];
|
|
|
|
extern s16 ttc_movtex_tris_small_surface_treadmill[];
|
|
|
|
extern s16 ssl_movtex_tris_quicksand_pit[];
|
|
|
|
extern Gfx ssl_dl_quicksand_pit_begin[];
|
|
|
|
extern Gfx ssl_dl_quicksand_pit_end[];
|
|
|
|
extern Gfx ssl_dl_quicksand_pit[];
|
|
|
|
extern s16 ssl_movtex_tris_pyramid_quicksand_pit[];
|
|
|
|
extern Gfx ssl_dl_pyramid_quicksand_pit_begin[];
|
|
|
|
extern Gfx ssl_dl_pyramid_quicksand_pit_end[];
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* MovtexObjects that have no color attributes per vertex (though the mesh
|
|
|
|
* as a whole can have a blend color).
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
struct MovtexObject gMovtexNonColored[] = {
|
|
|
|
// Inside the pyramid there is a sand pathway with the 5 secrets on it.
|
|
|
|
// pathway_front is the highest 'sand fall', pathway_floor is the horizontal
|
|
|
|
// sand stream and pathway_side is the lower 'sand fall'.
|
|
|
|
{ MOVTEX_PYRAMID_SAND_PATHWAY_FRONT, TEX_PYRAMID_SAND_SSL, 8,
|
|
|
|
ssl_movtex_tris_pyramid_sand_pathway_front, ssl_dl_pyramid_sand_pathway_begin,
|
|
|
|
ssl_dl_pyramid_sand_pathway_end, ssl_dl_pyramid_sand_pathway_front_end, 0xff, 0xff, 0xff, 0xff,
|
|
|
|
LAYER_TRANSPARENT_INTER },
|
|
|
|
{ MOVTEX_PYRAMID_SAND_PATHWAY_FLOOR, TEX_PYRAMID_SAND_SSL, 8,
|
|
|
|
ssl_movtex_tris_pyramid_sand_pathway_floor, ssl_dl_pyramid_sand_pathway_floor_begin,
|
|
|
|
ssl_dl_pyramid_sand_pathway_floor_end, ssl_dl_pyramid_sand_pathway_front_end, 0xff, 0xff, 0xff,
|
|
|
|
0xff, LAYER_OPAQUE_INTER },
|
|
|
|
{ MOVTEX_PYRAMID_SAND_PATHWAY_SIDE, TEX_PYRAMID_SAND_SSL, 6,
|
|
|
|
ssl_movtex_tris_pyramid_sand_pathway_side, ssl_dl_pyramid_sand_pathway_begin,
|
|
|
|
ssl_dl_pyramid_sand_pathway_end, ssl_dl_pyramid_sand_pathway_side_end, 0xff, 0xff, 0xff, 0xff,
|
|
|
|
LAYER_TRANSPARENT_INTER },
|
|
|
|
|
|
|
|
// The waterfall outside the castle
|
|
|
|
{ MOVTEX_CASTLE_WATERFALL, TEXTURE_WATER, 15, castle_grounds_movtex_tris_waterfall,
|
|
|
|
dl_waterbox_rgba16_begin, dl_waterbox_end, castle_grounds_dl_waterfall, 0xff, 0xff, 0xff, 0xb4,
|
|
|
|
LAYER_TRANSPARENT_INTER },
|
|
|
|
|
|
|
|
// Bowser in the Fire Sea has lava at 3 heights, lava_floor is the lowest
|
|
|
|
// and lava_second_section is the highest
|
|
|
|
{ MOVTEX_BITFS_LAVA_FIRST, TEXTURE_LAVA, 4, bitfs_movtex_tris_lava_first_section,
|
|
|
|
dl_waterbox_rgba16_begin, dl_waterbox_end, bitfs_dl_lava_sections, 0xff, 0xff, 0xff, 0xff,
|
|
|
|
LAYER_OPAQUE },
|
|
|
|
{ MOVTEX_BITFS_LAVA_SECOND, TEXTURE_LAVA, 4, bitfs_movtex_tris_lava_second_section,
|
|
|
|
dl_waterbox_rgba16_begin, dl_waterbox_end, bitfs_dl_lava_sections, 0xff, 0xff, 0xff, 0xb4,
|
|
|
|
LAYER_TRANSPARENT },
|
|
|
|
{ MOVTEX_BITFS_LAVA_FLOOR, TEXTURE_LAVA, 9, bitfs_movtex_tris_lava_floor, dl_waterbox_rgba16_begin,
|
|
|
|
dl_waterbox_end, bitfs_dl_lava_floor, 0xff, 0xff, 0xff, 0xb4, LAYER_TRANSPARENT },
|
|
|
|
|
|
|
|
// Lava floor in Lethal Lava Land and the lava fall in the volcano
|
|
|
|
//! Note that the lava floor in the volcano is actually a quad.
|
|
|
|
// The quad collection index LLL_MOVTEX_VOLCANO_FLOOR_LAVA is actually
|
|
|
|
// 2 | MOVTEX_AREA_LLL, suggesting that the lava floor of LLL used to be a
|
|
|
|
// quad too, with index 1.
|
|
|
|
// It was probably too large however, resulting in overflowing texture
|
|
|
|
// coordinates or other artifacts, so they converted it to a movtex
|
|
|
|
// mesh with 9 vertices, subdividing the rectangle into 4 smaller ones.
|
|
|
|
{ MOVTEX_LLL_LAVA_FLOOR, TEXTURE_LAVA, 9, lll_movtex_tris_lava_floor, dl_waterbox_rgba16_begin,
|
|
|
|
dl_waterbox_end, lll_dl_lava_floor, 0xff, 0xff, 0xff, 0xc8, LAYER_TRANSPARENT },
|
|
|
|
{ MOVTEX_VOLCANO_LAVA_FALL, TEXTURE_LAVA, 16, lll_movtex_tris_lavafall_volcano,
|
|
|
|
dl_waterbox_rgba16_begin, dl_waterbox_end, lll_dl_lavafall_volcano, 0xff, 0xff, 0xff, 0xb4,
|
|
|
|
LAYER_TRANSPARENT_INTER },
|
|
|
|
|
|
|
|
// Cavern of the metal Cap has a waterfall source above the switch platform,
|
|
|
|
// the stream, around the switch, and the waterfall that's the same as the one
|
|
|
|
// outside the castle. They are all part of the same mesh.
|
|
|
|
{ MOVTEX_COTMC_WATER, TEXTURE_WATER, 14, cotmc_movtex_tris_water, cotmc_dl_water_begin,
|
|
|
|
cotmc_dl_water_end, cotmc_dl_water, 0xff, 0xff, 0xff, 0xb4, LAYER_TRANSPARENT_INTER },
|
|
|
|
|
|
|
|
// Tall Tall mountain has water going from the top to the bottom of the mountain.
|
|
|
|
{ MOVTEX_TTM_BEGIN_WATERFALL, TEXTURE_WATER, 6, ttm_movtex_tris_begin_waterfall,
|
|
|
|
dl_waterbox_rgba16_begin, dl_waterbox_end, ttm_dl_waterfall, 0xff, 0xff, 0xff, 0xb4,
|
|
|
|
LAYER_TRANSPARENT },
|
|
|
|
{ MOVTEX_TTM_END_WATERFALL, TEXTURE_WATER, 6, ttm_movtex_tris_end_waterfall,
|
|
|
|
dl_waterbox_rgba16_begin, dl_waterbox_end, ttm_dl_waterfall, 0xff, 0xff, 0xff, 0xb4,
|
|
|
|
LAYER_TRANSPARENT },
|
|
|
|
{ MOVTEX_TTM_BEGIN_PUDDLE_WATERFALL, TEXTURE_WATER, 4, ttm_movtex_tris_begin_puddle_waterfall,
|
|
|
|
dl_waterbox_rgba16_begin, dl_waterbox_end, ttm_dl_bottom_waterfall, 0xff, 0xff, 0xff, 0xb4,
|
|
|
|
LAYER_TRANSPARENT_INTER },
|
|
|
|
{ MOVTEX_TTM_END_PUDDLE_WATERFALL, TEXTURE_WATER, 4, ttm_movtex_tris_end_puddle_waterfall,
|
|
|
|
dl_waterbox_rgba16_begin, dl_waterbox_end, ttm_dl_bottom_waterfall, 0xff, 0xff, 0xff, 0xb4,
|
|
|
|
LAYER_TRANSPARENT_INTER },
|
|
|
|
{ MOVTEX_TTM_PUDDLE_WATERFALL, TEXTURE_WATER, 8, ttm_movtex_tris_puddle_waterfall,
|
|
|
|
dl_waterbox_rgba16_begin, dl_waterbox_end, ttm_dl_puddle_waterfall, 0xff, 0xff, 0xff, 0xb4,
|
|
|
|
LAYER_TRANSPARENT_INTER },
|
|
|
|
{ 0x00000000, 0x00000000, 0, NULL, NULL, NULL, NULL, 0x00, 0x00, 0x00, 0x00, 0x00000000 },
|
|
|
|
};
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* MovtexObjects that have color attributes per vertex.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
struct MovtexObject gMovtexColored[] = {
|
|
|
|
{ MOVTEX_SSL_PYRAMID_SIDE, TEX_QUICKSAND_SSL, 12, ssl_movtex_tris_pyramid_quicksand,
|
|
|
|
ssl_dl_quicksand_begin, ssl_dl_quicksand_end, ssl_dl_pyramid_quicksand, 0xff, 0xff, 0xff, 0xff,
|
|
|
|
LAYER_OPAQUE },
|
|
|
|
{ MOVTEX_SSL_PYRAMID_CORNER, TEX_QUICKSAND_SSL, 16, ssl_movtex_tris_pyramid_corners_quicksand,
|
|
|
|
ssl_dl_quicksand_begin, ssl_dl_quicksand_end, ssl_dl_pyramid_corners_quicksand, 0xff, 0xff, 0xff,
|
|
|
|
0xff, LAYER_OPAQUE },
|
|
|
|
{ MOVTEX_SSL_COURSE_EDGE, TEX_QUICKSAND_SSL, 15, ssl_movtex_tris_sides_quicksand,
|
|
|
|
ssl_dl_quicksand_begin, ssl_dl_quicksand_end, ssl_dl_sides_quicksand, 0xff, 0xff, 0xff, 0xff,
|
|
|
|
LAYER_OPAQUE },
|
|
|
|
{ MOVTEX_TREADMILL_BIG, TEX_YELLOW_TRI_TTC, 12, ttc_movtex_tris_big_surface_treadmill,
|
|
|
|
ttc_dl_surface_treadmill_begin, ttc_dl_surface_treadmill_end, ttc_dl_surface_treadmill, 0xff,
|
|
|
|
0xff, 0xff, 0xff, LAYER_OPAQUE },
|
|
|
|
{ MOVTEX_TREADMILL_SMALL, TEX_YELLOW_TRI_TTC, 12, ttc_movtex_tris_small_surface_treadmill,
|
|
|
|
ttc_dl_surface_treadmill_begin, ttc_dl_surface_treadmill_end, ttc_dl_surface_treadmill, 0xff,
|
|
|
|
0xff, 0xff, 0xff, LAYER_OPAQUE },
|
|
|
|
{ 0x00000000, 0x00000000, 0, NULL, NULL, NULL, NULL, 0x00, 0x00, 0x00, 0x00, 0x00000000 },
|
|
|
|
};
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Treated identically to gMovtexColored.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
struct MovtexObject gMovtexColored2[] = {
|
|
|
|
{ MOVTEX_SSL_SAND_PIT_OUTSIDE, TEX_QUICKSAND_SSL, 8, ssl_movtex_tris_quicksand_pit,
|
|
|
|
ssl_dl_quicksand_pit_begin, ssl_dl_quicksand_pit_end, ssl_dl_quicksand_pit, 0xff, 0xff, 0xff,
|
|
|
|
0xff, LAYER_OPAQUE },
|
|
|
|
{ MOVTEX_SSL_SAND_PIT_PYRAMID, TEX_PYRAMID_SAND_SSL, 8, ssl_movtex_tris_pyramid_quicksand_pit,
|
|
|
|
ssl_dl_pyramid_quicksand_pit_begin, ssl_dl_pyramid_quicksand_pit_end, ssl_dl_quicksand_pit, 0xff,
|
|
|
|
0xff, 0xff, 0xff, LAYER_OPAQUE },
|
|
|
|
{ 0x00000000, 0x00000000, 0, NULL, NULL, NULL, NULL, 0x00, 0x00, 0x00, 0x00, 0x00000000 },
|
|
|
|
};
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Sets the initial water level in Wet-Dry World based on how high Mario
|
|
|
|
* jumped into the painting.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
Gfx *geo_wdw_set_initial_water_level(s32 callContext, UNUSED struct GraphNode *node,
|
|
|
|
UNUSED f32 mtx[4][4]) {
|
|
|
|
s32 i;
|
|
|
|
UNUSED u8 unused[] = { 1, 0, 4, 0, 7, 0, 10, 0 };
|
|
|
|
s16 wdwWaterHeight;
|
|
|
|
|
|
|
|
// Why was this global variable needed when they could just check for GEO_CONTEXT_AREA_LOAD?
|
|
|
|
if (callContext != GEO_CONTEXT_RENDER) {
|
|
|
|
gWdwWaterLevelSet = 0;
|
|
|
|
} else if (callContext == GEO_CONTEXT_RENDER && gEnvironmentRegions != NULL
|
|
|
|
&& gWdwWaterLevelSet == 0) {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (gPaintingMarioYEntry <= 1382.4) {
|
2019-08-25 04:46:40 +00:00
|
|
|
wdwWaterHeight = 31;
|
2019-09-01 19:50:50 +00:00
|
|
|
} else if (gPaintingMarioYEntry >= 1600.0) {
|
2019-08-25 04:46:40 +00:00
|
|
|
wdwWaterHeight = 2816;
|
2019-09-01 19:50:50 +00:00
|
|
|
} else {
|
2019-08-25 04:46:40 +00:00
|
|
|
wdwWaterHeight = 1024;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
for (i = 0; i < *gEnvironmentRegions; i++) {
|
|
|
|
gEnvironmentRegions[i * 6 + 6] = wdwWaterHeight;
|
|
|
|
}
|
|
|
|
gWdwWaterLevelSet = 1;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Update moving texture counters that determine when to update the coordinates.
|
|
|
|
* Textures update when gMovtexCounterPrev != gMovtexCounter.
|
|
|
|
* This ensures water / sand flow stops when the game pauses.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
Gfx *geo_movtex_pause_control(s32 callContext, UNUSED struct GraphNode *node, UNUSED f32 mtx[4][4]) {
|
|
|
|
if (callContext != GEO_CONTEXT_RENDER) {
|
|
|
|
gMovtexCounterPrev = gAreaUpdateCounter - 1;
|
|
|
|
gMovtexCounter = gAreaUpdateCounter;
|
|
|
|
} else {
|
|
|
|
gMovtexCounterPrev = gMovtexCounter;
|
|
|
|
gMovtexCounter = gAreaUpdateCounter;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Make a vertex that's part of a quad with rotating texture.
|
|
|
|
* verts: array of RSP vertices
|
|
|
|
* n: index in 'verts' where the vertex is written
|
|
|
|
* x, y, z: position
|
|
|
|
* rot: base rotation of the texture
|
|
|
|
* rotOffset: gets added to base rotation
|
|
|
|
* scale: how often the texture repeats, 1 = no repeat
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
void movtex_make_quad_vertex(Vtx *verts, s32 index, s16 x, s16 y, s16 z, s16 rot, s16 rotOffset,
|
|
|
|
f32 scale, u8 alpha) {
|
|
|
|
s16 s = 32.0 * (32.0 * scale - 1.0) * sins(rot + rotOffset);
|
|
|
|
s16 t = 32.0 * (32.0 * scale - 1.0) * coss(rot + rotOffset);
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (gMovtexVtxColor == MOVTEX_VTX_COLOR_YELLOW) {
|
2019-08-25 04:46:40 +00:00
|
|
|
make_vertex(verts, index, x, y, z, s, t, 255, 255, 0, alpha);
|
2019-09-01 19:50:50 +00:00
|
|
|
} else if (gMovtexVtxColor == MOVTEX_VTX_COLOR_RED) {
|
2019-08-25 04:46:40 +00:00
|
|
|
make_vertex(verts, index, x, y, z, s, t, 255, 0, 0, alpha);
|
2019-09-01 19:50:50 +00:00
|
|
|
} else {
|
2019-08-25 04:46:40 +00:00
|
|
|
make_vertex(verts, index, x, y, z, s, t, 255, 255, 255, alpha);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Represents a single flat quad with a rotating texture
|
|
|
|
* Stores x and z for 4 vertices, though it is often just a rectangle.
|
|
|
|
* Does not store the y-position, since that can be dynamic for water levels.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
struct MovtexQuad {
|
|
|
|
/// the current texture rotation in this quad
|
|
|
|
s16 rot;
|
|
|
|
/// gets added to rot every frame
|
|
|
|
s16 rotspeed;
|
|
|
|
/// the amount of times the texture repeats. 1 = no repeat.
|
|
|
|
s16 scale;
|
|
|
|
/// Coordinates of vertices
|
|
|
|
s16 x1;
|
|
|
|
s16 z1;
|
|
|
|
s16 x2;
|
|
|
|
s16 z2;
|
|
|
|
s16 x3;
|
|
|
|
s16 z3;
|
|
|
|
s16 x4;
|
|
|
|
s16 z4;
|
|
|
|
s16 rotDir; /// if 1, it rotates counter-clockwise
|
|
|
|
s16 alpha; /// opacity, 255 = fully opaque
|
|
|
|
s16 textureId; /// texture id
|
|
|
|
};
|
|
|
|
|
|
|
|
/// Variable for a little optimization: only set the texture when it differs from the previous texture
|
|
|
|
s16 gMovetexLastTextureId;
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Generates and returns a display list for a single MovtexQuad at height y.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
Gfx *movtex_gen_from_quad(s16 y, struct MovtexQuad *quad) {
|
|
|
|
s16 rot;
|
|
|
|
s16 rotspeed = quad->rotspeed;
|
|
|
|
s16 scale = quad->scale;
|
|
|
|
s16 x1 = quad->x1;
|
|
|
|
s16 z1 = quad->z1;
|
|
|
|
s16 x2 = quad->x2;
|
|
|
|
s16 z2 = quad->z2;
|
|
|
|
s16 x3 = quad->x3;
|
|
|
|
s16 z3 = quad->z3;
|
|
|
|
s16 x4 = quad->x4;
|
|
|
|
s16 z4 = quad->z4;
|
|
|
|
s16 rotDir = quad->rotDir;
|
|
|
|
s16 alpha = quad->alpha;
|
|
|
|
s16 textureId = quad->textureId;
|
|
|
|
Vtx *verts = alloc_display_list(4 * sizeof(*verts));
|
|
|
|
Gfx *gfxHead;
|
|
|
|
Gfx *gfx;
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (textureId == gMovetexLastTextureId) {
|
2019-08-25 04:46:40 +00:00
|
|
|
gfxHead = alloc_display_list(3 * sizeof(*gfxHead));
|
2019-09-01 19:50:50 +00:00
|
|
|
} else {
|
2019-08-25 04:46:40 +00:00
|
|
|
gfxHead = alloc_display_list(8 * sizeof(*gfxHead));
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (gfxHead == NULL || verts == NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
return NULL;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
gfx = gfxHead;
|
2019-09-01 19:50:50 +00:00
|
|
|
if (gMovtexCounter != gMovtexCounterPrev) {
|
2019-08-25 04:46:40 +00:00
|
|
|
quad->rot += rotspeed;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
rot = quad->rot;
|
|
|
|
if (rotDir == ROTATE_CLOCKWISE) {
|
|
|
|
movtex_make_quad_vertex(verts, 0, x1, y, z1, rot, 0, scale, alpha);
|
|
|
|
movtex_make_quad_vertex(verts, 1, x2, y, z2, rot, 16384, scale, alpha);
|
|
|
|
movtex_make_quad_vertex(verts, 2, x3, y, z3, rot, -32768, scale, alpha);
|
|
|
|
movtex_make_quad_vertex(verts, 3, x4, y, z4, rot, -16384, scale, alpha);
|
2019-10-05 19:08:05 +00:00
|
|
|
} else { // ROTATE_COUNTER_CLOCKWISE
|
2019-08-25 04:46:40 +00:00
|
|
|
movtex_make_quad_vertex(verts, 0, x1, y, z1, rot, 0, scale, alpha);
|
|
|
|
movtex_make_quad_vertex(verts, 1, x2, y, z2, rot, -16384, scale, alpha);
|
|
|
|
movtex_make_quad_vertex(verts, 2, x3, y, z3, rot, -32768, scale, alpha);
|
|
|
|
movtex_make_quad_vertex(verts, 3, x4, y, z4, rot, 16384, scale, alpha);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only add commands to change the texture when necessary
|
|
|
|
if (textureId != gMovetexLastTextureId) {
|
2019-12-02 02:52:53 +00:00
|
|
|
if (textureId == TEXTURE_MIST) { // an ia16 texture
|
2019-08-25 04:46:40 +00:00
|
|
|
if (0) {
|
|
|
|
}
|
2020-02-03 05:51:26 +00:00
|
|
|
gLoadBlockTexture(gfx++, 32, 32, G_IM_FMT_IA, gMovtexIdToTexture[textureId]);
|
2019-10-05 19:08:05 +00:00
|
|
|
} else { // any rgba16 texture
|
2020-02-03 05:51:26 +00:00
|
|
|
gLoadBlockTexture(gfx++, 32, 32, G_IM_FMT_RGBA, gMovtexIdToTexture[textureId]);
|
2019-10-05 19:08:05 +00:00
|
|
|
if (0) {
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
gMovetexLastTextureId = textureId;
|
|
|
|
}
|
2019-10-05 19:08:05 +00:00
|
|
|
gSPVertex(gfx++, VIRTUAL_TO_PHYSICAL2(verts), 4, 0);
|
|
|
|
gSPDisplayList(gfx++, dl_draw_quad_verts_0123);
|
|
|
|
gSPEndDisplayList(gfx);
|
|
|
|
return gfxHead;
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Generate a display list drawing an array of MoxtexQuad at height 'y'.
|
|
|
|
* y: y position of the quads
|
|
|
|
* quadArrSegmented: a segmented address to an array of s16. The first number
|
|
|
|
* is the number of entries, followed by that number of MovtexQuad structs.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
Gfx *movtex_gen_from_quad_array(s16 y, void *quadArrSegmented) {
|
|
|
|
s16 *quadArr = segmented_to_virtual(quadArrSegmented);
|
|
|
|
s16 numLists = quadArr[0];
|
|
|
|
Gfx *gfxHead = alloc_display_list((numLists + 1) * sizeof(*gfxHead));
|
|
|
|
Gfx *gfx = gfxHead;
|
|
|
|
Gfx *subList;
|
|
|
|
s32 i;
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (gfxHead == NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
return NULL;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
for (i = 0; i < numLists; i++) {
|
|
|
|
// quadArr is an array of s16, so sizeof(MovtexQuad) gets divided by 2
|
|
|
|
subList = movtex_gen_from_quad(
|
|
|
|
y, (struct MovtexQuad *) (&quadArr[i * (sizeof(struct MovtexQuad) / 2) + 1]));
|
2019-10-05 19:08:05 +00:00
|
|
|
if (subList != NULL) {
|
|
|
|
gSPDisplayList(gfx++, VIRTUAL_TO_PHYSICAL(subList));
|
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
gSPEndDisplayList(gfx);
|
|
|
|
return gfxHead;
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Generate the display list for a list of quads by searching through a collection
|
|
|
|
* for a given id.
|
|
|
|
* id: id of quad array to generate a list for
|
|
|
|
* y: height at which the quads are drawn
|
|
|
|
* movetexQuadsSegmented: segmented address to the MovtexQuadCollection array
|
|
|
|
* that will be searched.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
Gfx *movtex_gen_quads_id(s16 id, s16 y, void *movetexQuadsSegmented) {
|
|
|
|
struct MovtexQuadCollection *collection = segmented_to_virtual(movetexQuadsSegmented);
|
|
|
|
s32 i = 0;
|
|
|
|
|
|
|
|
while (collection[i].id != -1) {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (collection[i].id == id) {
|
2019-08-25 04:46:40 +00:00
|
|
|
return movtex_gen_from_quad_array(y, collection[i].quadArraySegmented);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
i++;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern u8 bbh_movtex_merry_go_round_water_entrance[];
|
|
|
|
extern u8 bbh_movtex_merry_go_round_water_side[];
|
|
|
|
extern u8 ccm_movtex_penguin_puddle_water[];
|
|
|
|
extern u8 inside_castle_movtex_green_room_water[];
|
|
|
|
extern u8 inside_castle_movtex_moat_water[];
|
|
|
|
extern u8 hmc_movtex_dorrie_pool_water[];
|
|
|
|
extern u8 hmc_movtex_toxic_maze_mist[];
|
|
|
|
extern u8 ssl_movtex_puddle_water[];
|
|
|
|
extern u8 ssl_movtex_toxbox_quicksand_mist[];
|
|
|
|
extern u8 sl_movtex_water[];
|
|
|
|
extern u8 wdw_movtex_area1_water[];
|
|
|
|
extern u8 wdw_movtex_area2_water[];
|
|
|
|
extern u8 jrb_movtex_water[];
|
|
|
|
extern u8 jrb_movtex_intial_mist[];
|
|
|
|
extern u8 jrb_movtex_sinked_boat_water[];
|
|
|
|
extern u8 thi_movtex_area1_water[];
|
|
|
|
extern u8 thi_movtex_area2_water[];
|
|
|
|
extern u8 castle_grounds_movtex_water[];
|
|
|
|
extern u8 lll_movtex_volcano_floor_lava[];
|
|
|
|
extern u8 ddd_movtex_area1_water[];
|
|
|
|
extern u8 ddd_movtex_area2_water[];
|
|
|
|
extern u8 wf_movtex_water[];
|
|
|
|
extern u8 castle_courtyard_movtex_star_statue_water[];
|
|
|
|
extern u8 ttm_movtex_puddle[];
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Find the quadCollection for a given quad collection id.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
void *get_quad_collection_from_id(u32 id) {
|
|
|
|
switch (id) {
|
|
|
|
case BBH_MOVTEX_MERRY_GO_ROUND_WATER_ENTRANCE:
|
|
|
|
return bbh_movtex_merry_go_round_water_entrance;
|
|
|
|
case BBH_MOVTEX_MERRY_GO_ROUND_WATER_SIDE:
|
|
|
|
return bbh_movtex_merry_go_round_water_side;
|
|
|
|
case CCM_MOVTEX_PENGUIN_PUDDLE_WATER:
|
|
|
|
return ccm_movtex_penguin_puddle_water;
|
|
|
|
case INSIDE_CASTLE_MOVTEX_GREEN_ROOM_WATER:
|
|
|
|
return inside_castle_movtex_green_room_water;
|
|
|
|
case INSIDE_CASTLE_MOVTEX_MOAT_WATER:
|
|
|
|
return inside_castle_movtex_moat_water;
|
|
|
|
case HMC_MOVTEX_DORRIE_POOL_WATER:
|
|
|
|
return hmc_movtex_dorrie_pool_water;
|
|
|
|
case HMC_MOVTEX_TOXIC_MAZE_MIST:
|
|
|
|
return hmc_movtex_toxic_maze_mist;
|
|
|
|
case SSL_MOVTEX_PUDDLE_WATER:
|
|
|
|
return ssl_movtex_puddle_water;
|
|
|
|
case SSL_MOVTEX_TOXBOX_QUICKSAND_MIST:
|
|
|
|
return ssl_movtex_toxbox_quicksand_mist;
|
|
|
|
case SL_MOVTEX_WATER:
|
|
|
|
return sl_movtex_water;
|
|
|
|
case WDW_MOVTEX_AREA1_WATER:
|
|
|
|
return wdw_movtex_area1_water;
|
|
|
|
case WDW_MOVTEX_AREA2_WATER:
|
|
|
|
return wdw_movtex_area2_water;
|
|
|
|
case JRB_MOVTEX_WATER:
|
|
|
|
return jrb_movtex_water;
|
|
|
|
case JRB_MOVTEX_INTIAL_MIST:
|
|
|
|
return jrb_movtex_intial_mist;
|
|
|
|
case JRB_MOVTEX_SINKED_BOAT_WATER:
|
|
|
|
return jrb_movtex_sinked_boat_water;
|
|
|
|
case THI_MOVTEX_AREA1_WATER:
|
|
|
|
return thi_movtex_area1_water;
|
|
|
|
case THI_MOVTEX_AREA2_WATER:
|
|
|
|
return thi_movtex_area2_water;
|
|
|
|
case CASTLE_GROUNDS_MOVTEX_WATER:
|
|
|
|
return castle_grounds_movtex_water;
|
|
|
|
case LLL_MOVTEX_VOLCANO_FLOOR_LAVA:
|
|
|
|
return lll_movtex_volcano_floor_lava;
|
|
|
|
case DDD_MOVTEX_AREA1_WATER:
|
|
|
|
return ddd_movtex_area1_water;
|
|
|
|
case DDD_MOVTEX_AREA2_WATER:
|
|
|
|
return ddd_movtex_area2_water;
|
|
|
|
case WF_MOVTEX_WATER:
|
|
|
|
return wf_movtex_water;
|
|
|
|
case CASTLE_COURTYARD_MOVTEX_STAR_STATUE_WATER:
|
|
|
|
return castle_courtyard_movtex_star_statue_water;
|
|
|
|
case TTM_MOVTEX_PUDDLE:
|
|
|
|
return ttm_movtex_puddle;
|
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Write to 'gfx' a command to set the current texture format for the given
|
|
|
|
* quadCollection.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
void movtex_change_texture_format(u32 quadCollectionId, Gfx **gfx) {
|
|
|
|
switch (quadCollectionId) {
|
|
|
|
case HMC_MOVTEX_TOXIC_MAZE_MIST:
|
2019-10-05 19:08:05 +00:00
|
|
|
gSPDisplayList((*gfx)++, dl_waterbox_ia16_begin);
|
|
|
|
break;
|
2019-08-25 04:46:40 +00:00
|
|
|
case SSL_MOVTEX_TOXBOX_QUICKSAND_MIST:
|
2019-10-05 19:08:05 +00:00
|
|
|
gSPDisplayList((*gfx)++, dl_waterbox_ia16_begin);
|
|
|
|
break;
|
2019-08-25 04:46:40 +00:00
|
|
|
case JRB_MOVTEX_INTIAL_MIST:
|
2019-10-05 19:08:05 +00:00
|
|
|
gSPDisplayList((*gfx)++, dl_waterbox_ia16_begin);
|
|
|
|
break;
|
2019-08-25 04:46:40 +00:00
|
|
|
default:
|
2019-10-05 19:08:05 +00:00
|
|
|
gSPDisplayList((*gfx)++, dl_waterbox_rgba16_begin);
|
|
|
|
break;
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Geo script responsible for drawing quads with a moving texture at the height
|
|
|
|
* of the corresponding water region. The node's parameter determines which quad
|
|
|
|
* collection is drawn, see moving_texture.h.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
Gfx *geo_movtex_draw_water_regions(s32 callContext, struct GraphNode *node, UNUSED f32 mtx[4][4]) {
|
|
|
|
Gfx *gfxHead = NULL;
|
|
|
|
Gfx *gfx = NULL;
|
|
|
|
Gfx *subList;
|
|
|
|
void *quadCollection;
|
|
|
|
struct GraphNodeGenerated *asGenerated;
|
|
|
|
s16 numWaterBoxes;
|
|
|
|
s16 waterId;
|
|
|
|
s16 waterY;
|
|
|
|
s32 i;
|
|
|
|
|
|
|
|
if (callContext == GEO_CONTEXT_RENDER) {
|
|
|
|
gMovtexVtxColor = MOVTEX_VTX_COLOR_DEFAULT;
|
2019-09-01 19:50:50 +00:00
|
|
|
if (gEnvironmentRegions == NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
return NULL;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
numWaterBoxes = gEnvironmentRegions[0];
|
|
|
|
gfxHead = alloc_display_list((numWaterBoxes + 3) * sizeof(*gfxHead));
|
2019-09-01 19:50:50 +00:00
|
|
|
if (gfxHead == NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
return NULL;
|
2019-09-01 19:50:50 +00:00
|
|
|
} else {
|
2019-08-25 04:46:40 +00:00
|
|
|
gfx = gfxHead;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
asGenerated = (struct GraphNodeGenerated *) node;
|
|
|
|
if (asGenerated->parameter == JRB_MOVTEX_INTIAL_MIST) {
|
2020-01-03 15:38:57 +00:00
|
|
|
if (gLakituState.goalPos[1] < 1024.0) { // if camera under water
|
2019-08-25 04:46:40 +00:00
|
|
|
return NULL;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
|
|
|
if (save_file_get_star_flags(gCurrSaveFileNum - 1, 2) & 1) { // first level in JRB complete
|
2019-08-25 04:46:40 +00:00
|
|
|
return NULL;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
} else if (asGenerated->parameter == HMC_MOVTEX_TOXIC_MAZE_MIST) {
|
|
|
|
gMovtexVtxColor = MOVTEX_VTX_COLOR_YELLOW;
|
|
|
|
} else if (asGenerated->parameter == SSL_MOVTEX_TOXBOX_QUICKSAND_MIST) {
|
|
|
|
gMovtexVtxColor = MOVTEX_VTX_COLOR_RED;
|
|
|
|
}
|
|
|
|
quadCollection = get_quad_collection_from_id(asGenerated->parameter);
|
2019-09-01 19:50:50 +00:00
|
|
|
if (quadCollection == NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
return NULL;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
asGenerated->fnNode.node.flags =
|
|
|
|
(asGenerated->fnNode.node.flags & 0xFF) | (LAYER_TRANSPARENT_INTER << 8);
|
|
|
|
|
|
|
|
movtex_change_texture_format(asGenerated->parameter, &gfx);
|
|
|
|
gMovetexLastTextureId = -1;
|
|
|
|
for (i = 0; i < numWaterBoxes; i++) {
|
|
|
|
waterId = gEnvironmentRegions[i * 6 + 1];
|
|
|
|
waterY = gEnvironmentRegions[i * 6 + 6];
|
|
|
|
subList = movtex_gen_quads_id(waterId, waterY, quadCollection);
|
|
|
|
if (subList != NULL)
|
|
|
|
gSPDisplayList(gfx++, VIRTUAL_TO_PHYSICAL(subList));
|
|
|
|
}
|
|
|
|
gSPDisplayList(gfx++, dl_waterbox_end);
|
|
|
|
gSPEndDisplayList(gfx);
|
|
|
|
}
|
|
|
|
return gfxHead;
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Updates a movtex mesh by adding the movtex's speed to the horizontal or
|
|
|
|
* vertical texture coordinates depending on 'attr'.
|
|
|
|
* movtexVerts: vertices to update
|
|
|
|
* attr: which attribute to change
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
void update_moving_texture_offset(s16 *movtexVerts, s32 attr) {
|
|
|
|
s16 movSpeed = movtexVerts[MOVTEX_ATTR_SPEED];
|
|
|
|
s16 *curOffset = movtexVerts + attr;
|
|
|
|
|
|
|
|
if (gMovtexCounter != gMovtexCounterPrev) {
|
|
|
|
*curOffset += movSpeed;
|
|
|
|
// note that texture coordinates are 6.10 fixed point, so this does modulo 1
|
2019-09-01 19:50:50 +00:00
|
|
|
if (*curOffset >= 1024) {
|
2019-08-25 04:46:40 +00:00
|
|
|
*curOffset -= 1024;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
|
|
|
if (*curOffset <= -1024) {
|
2019-08-25 04:46:40 +00:00
|
|
|
*curOffset += 1024;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Make the first vertex of a moving texture with index 0.
|
|
|
|
* This vertex is the base of all vertices with index > 0, which use this
|
|
|
|
* vertex's coordinates as base on which to apply offset.
|
|
|
|
* The first vertex has offset 0 by definition, simplifying the calculations a bit.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
void movtex_write_vertex_first(Vtx *vtx, s16 *movtexVerts, struct MovtexObject *c, s8 attrLayout) {
|
|
|
|
s16 x = movtexVerts[MOVTEX_ATTR_X];
|
|
|
|
s16 y = movtexVerts[MOVTEX_ATTR_Y];
|
|
|
|
s16 z = movtexVerts[MOVTEX_ATTR_Z];
|
|
|
|
u8 alpha = c->a;
|
|
|
|
u8 r1;
|
|
|
|
u8 g1;
|
|
|
|
u8 b1;
|
|
|
|
s8 r2;
|
|
|
|
s8 g2;
|
|
|
|
s8 b2;
|
|
|
|
s16 s;
|
|
|
|
s16 t;
|
|
|
|
|
|
|
|
switch (attrLayout) {
|
|
|
|
case MOVTEX_LAYOUT_NOCOLOR:
|
|
|
|
r1 = c->r;
|
|
|
|
g1 = c->g;
|
|
|
|
b1 = c->b;
|
|
|
|
s = movtexVerts[MOVTEX_ATTR_NOCOLOR_S];
|
|
|
|
t = movtexVerts[MOVTEX_ATTR_NOCOLOR_T];
|
|
|
|
make_vertex(vtx, 0, x, y, z, s, t, r1, g1, b1, alpha);
|
|
|
|
break;
|
|
|
|
case MOVTEX_LAYOUT_COLORED:
|
|
|
|
r2 = movtexVerts[MOVTEX_ATTR_COLORED_R];
|
|
|
|
g2 = movtexVerts[MOVTEX_ATTR_COLORED_G];
|
|
|
|
b2 = movtexVerts[MOVTEX_ATTR_COLORED_B];
|
|
|
|
s = movtexVerts[MOVTEX_ATTR_COLORED_S];
|
|
|
|
t = movtexVerts[MOVTEX_ATTR_COLORED_T];
|
|
|
|
make_vertex(vtx, 0, x, y, z, s, t, r2, g2, b2, alpha);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Make a vertex with index > 0. The vertex with index 0 is made in
|
|
|
|
* movtex_write_vertex_first and subsequent vertices use vertex 0 as a base
|
|
|
|
* for their texture coordinates.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
void movtex_write_vertex_index(Vtx *verts, s32 index, s16 *movtexVerts, struct MovtexObject *d,
|
|
|
|
s8 attrLayout) {
|
|
|
|
u8 alpha = d->a;
|
|
|
|
s16 x;
|
|
|
|
s16 y;
|
|
|
|
s16 z;
|
|
|
|
s16 baseS;
|
|
|
|
s16 baseT;
|
|
|
|
s16 s;
|
|
|
|
s16 t;
|
|
|
|
s16 offS;
|
|
|
|
s16 offT;
|
|
|
|
u8 r1;
|
|
|
|
u8 g1;
|
|
|
|
u8 b1;
|
|
|
|
s8 r2;
|
|
|
|
s8 g2;
|
|
|
|
s8 b2;
|
|
|
|
|
|
|
|
switch (attrLayout) {
|
|
|
|
case MOVTEX_LAYOUT_NOCOLOR:
|
|
|
|
x = movtexVerts[index * 5 + MOVTEX_ATTR_X];
|
|
|
|
y = movtexVerts[index * 5 + MOVTEX_ATTR_Y];
|
|
|
|
z = movtexVerts[index * 5 + MOVTEX_ATTR_Z];
|
|
|
|
baseS = movtexVerts[MOVTEX_ATTR_NOCOLOR_S];
|
|
|
|
baseT = movtexVerts[MOVTEX_ATTR_NOCOLOR_T];
|
|
|
|
offS = movtexVerts[index * 5 + MOVTEX_ATTR_NOCOLOR_S];
|
|
|
|
offT = movtexVerts[index * 5 + MOVTEX_ATTR_NOCOLOR_T];
|
|
|
|
s = baseS + ((offS * 32) * 32U);
|
|
|
|
t = baseT + ((offT * 32) * 32U);
|
|
|
|
r1 = d->r;
|
|
|
|
g1 = d->g;
|
|
|
|
b1 = d->b;
|
|
|
|
make_vertex(verts, index, x, y, z, s, t, r1, g1, b1, alpha);
|
|
|
|
break;
|
|
|
|
case MOVTEX_LAYOUT_COLORED:
|
|
|
|
x = movtexVerts[index * 8 + MOVTEX_ATTR_X];
|
|
|
|
y = movtexVerts[index * 8 + MOVTEX_ATTR_Y];
|
|
|
|
z = movtexVerts[index * 8 + MOVTEX_ATTR_Z];
|
|
|
|
baseS = movtexVerts[7];
|
|
|
|
baseT = movtexVerts[8];
|
|
|
|
offS = movtexVerts[index * 8 + 7];
|
|
|
|
offT = movtexVerts[index * 8 + 8];
|
|
|
|
s = baseS + ((offS * 32) * 32U);
|
|
|
|
t = baseT + ((offT * 32) * 32U);
|
|
|
|
r2 = movtexVerts[index * 8 + MOVTEX_ATTR_COLORED_R];
|
|
|
|
g2 = movtexVerts[index * 8 + MOVTEX_ATTR_COLORED_G];
|
|
|
|
b2 = movtexVerts[index * 8 + MOVTEX_ATTR_COLORED_B];
|
|
|
|
make_vertex(verts, index, x, y, z, s, t, r2, g2, b2, alpha);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Generate a displaylist for a MovtexObject.
|
|
|
|
* 'attrLayout' is one of MOVTEX_LAYOUT_NOCOLOR and MOVTEX_LAYOUT_COLORED.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
Gfx *movtex_gen_list(s16 *movtexVerts, struct MovtexObject *movtexList, s8 attrLayout) {
|
|
|
|
Vtx *verts = alloc_display_list(movtexList->vtx_count * sizeof(*verts));
|
|
|
|
Gfx *gfxHead = alloc_display_list(11 * sizeof(*gfxHead));
|
|
|
|
Gfx *gfx = gfxHead;
|
|
|
|
s32 i;
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (verts == NULL || gfxHead == NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
return NULL;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
movtex_write_vertex_first(verts, movtexVerts, movtexList, attrLayout);
|
2019-09-01 19:50:50 +00:00
|
|
|
for (i = 1; i < movtexList->vtx_count; i++) {
|
2019-08-25 04:46:40 +00:00
|
|
|
movtex_write_vertex_index(verts, i, movtexVerts, movtexList, attrLayout);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2019-10-05 19:08:05 +00:00
|
|
|
gSPDisplayList(gfx++, movtexList->beginDl);
|
2020-02-03 05:51:26 +00:00
|
|
|
gLoadBlockTexture(gfx++, 32, 32, G_IM_FMT_RGBA, gMovtexIdToTexture[movtexList->textureId]);
|
2019-10-05 19:08:05 +00:00
|
|
|
gSPVertex(gfx++, VIRTUAL_TO_PHYSICAL2(verts), movtexList->vtx_count, 0);
|
|
|
|
gSPDisplayList(gfx++, movtexList->triDl);
|
|
|
|
gSPDisplayList(gfx++, movtexList->endDl);
|
|
|
|
gSPEndDisplayList(gfx);
|
|
|
|
return gfxHead;
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Function for a geo node that draws a MovtexObject in the gMovtexNonColored list.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
Gfx *geo_movtex_draw_nocolor(s32 callContext, struct GraphNode *node, UNUSED f32 mtx[4][4]) {
|
|
|
|
s32 i;
|
|
|
|
s16 *movtexVerts;
|
|
|
|
struct GraphNodeGenerated *asGenerated;
|
|
|
|
Gfx *gfx = NULL;
|
|
|
|
|
|
|
|
if (callContext == GEO_CONTEXT_RENDER) {
|
|
|
|
i = 0;
|
|
|
|
asGenerated = (struct GraphNodeGenerated *) node;
|
|
|
|
while (gMovtexNonColored[i].movtexVerts != 0) {
|
|
|
|
if (gMovtexNonColored[i].geoId == asGenerated->parameter) {
|
|
|
|
asGenerated->fnNode.node.flags =
|
|
|
|
(asGenerated->fnNode.node.flags & 0xFF) | (gMovtexNonColored[i].layer << 8);
|
|
|
|
movtexVerts = segmented_to_virtual(gMovtexNonColored[i].movtexVerts);
|
|
|
|
update_moving_texture_offset(movtexVerts, MOVTEX_ATTR_NOCOLOR_S);
|
|
|
|
gfx = movtex_gen_list(movtexVerts, &gMovtexNonColored[i],
|
|
|
|
MOVTEX_LAYOUT_NOCOLOR); // no perVertex colors
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return gfx;
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Function for a geo node that draws a MovtexObject in the gMovtexColored list.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
Gfx *geo_movtex_draw_colored(s32 callContext, struct GraphNode *node, UNUSED f32 mtx[4][4]) {
|
|
|
|
s32 i;
|
|
|
|
s16 *movtexVerts;
|
|
|
|
struct GraphNodeGenerated *asGenerated;
|
|
|
|
Gfx *gfx = NULL;
|
|
|
|
|
|
|
|
if (callContext == GEO_CONTEXT_RENDER) {
|
|
|
|
i = 0;
|
|
|
|
asGenerated = (struct GraphNodeGenerated *) node;
|
|
|
|
while (gMovtexColored[i].movtexVerts != 0) {
|
|
|
|
if (gMovtexColored[i].geoId == asGenerated->parameter) {
|
|
|
|
asGenerated->fnNode.node.flags =
|
|
|
|
(asGenerated->fnNode.node.flags & 0xFF) | (gMovtexColored[i].layer << 8);
|
|
|
|
movtexVerts = segmented_to_virtual(gMovtexColored[i].movtexVerts);
|
|
|
|
update_moving_texture_offset(movtexVerts, MOVTEX_ATTR_COLORED_S);
|
|
|
|
gfx = movtex_gen_list(movtexVerts, &gMovtexColored[i], MOVTEX_LAYOUT_COLORED);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return gfx;
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Function for a geo node that draws a MovtexObject in the gMovtexColored list,
|
|
|
|
* but it doesn't call update_moving_texture_offset since that happens in
|
|
|
|
* geo_movtex_update_horizontal. This is for when a MovtexObject has multiple
|
|
|
|
* instances (like TTC treadmills) so you don't want the animation speed to
|
|
|
|
* increase the more instances there are.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
Gfx *geo_movtex_draw_colored_no_update(s32 callContext, struct GraphNode *node, UNUSED f32 mtx[4][4]) {
|
|
|
|
s32 i;
|
|
|
|
s16 *movtexVerts;
|
|
|
|
struct GraphNodeGenerated *asGenerated;
|
|
|
|
Gfx *gfx = NULL;
|
|
|
|
|
|
|
|
if (callContext == GEO_CONTEXT_RENDER) {
|
|
|
|
i = 0;
|
|
|
|
asGenerated = (struct GraphNodeGenerated *) node;
|
|
|
|
while (gMovtexColored[i].movtexVerts != 0) {
|
|
|
|
if (gMovtexColored[i].geoId == asGenerated->parameter) {
|
|
|
|
asGenerated->fnNode.node.flags =
|
|
|
|
(asGenerated->fnNode.node.flags & 0xFF) | (gMovtexColored[i].layer << 8);
|
|
|
|
movtexVerts = segmented_to_virtual(gMovtexColored[i].movtexVerts);
|
|
|
|
gfx = movtex_gen_list(movtexVerts, &gMovtexColored[i], MOVTEX_LAYOUT_COLORED);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return gfx;
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Exact copy of geo_movtex_draw_colored_no_update, but now using the gMovtexColored2 array.
|
|
|
|
* Used for the sand pits in SSL, both outside and inside the pyramid.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
Gfx *geo_movtex_draw_colored_2_no_update(s32 callContext, struct GraphNode *node,
|
|
|
|
UNUSED f32 mtx[4][4]) {
|
|
|
|
s32 i;
|
|
|
|
s16 *movtexVerts;
|
|
|
|
struct GraphNodeGenerated *asGenerated;
|
|
|
|
Gfx *gfx = NULL;
|
|
|
|
|
|
|
|
if (callContext == GEO_CONTEXT_RENDER) {
|
|
|
|
i = 0;
|
|
|
|
asGenerated = (struct GraphNodeGenerated *) node;
|
|
|
|
while (gMovtexColored2[i].movtexVerts != 0) {
|
|
|
|
if (gMovtexColored2[i].geoId == asGenerated->parameter) {
|
|
|
|
asGenerated->fnNode.node.flags =
|
|
|
|
(asGenerated->fnNode.node.flags & 0xFF) | (gMovtexColored2[i].layer << 8);
|
|
|
|
movtexVerts = segmented_to_virtual(gMovtexColored2[i].movtexVerts);
|
|
|
|
gfx = movtex_gen_list(movtexVerts, &gMovtexColored2[i], MOVTEX_LAYOUT_COLORED);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return gfx;
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* Make textures move horizontally by simply adding a number to the 's' texture coordinate.
|
|
|
|
* Used for:
|
|
|
|
* - treadmills in Tick Tock Clock
|
|
|
|
* - sand pits outside and inside the pyramid in Shifting Sand
|
|
|
|
* Note that the drawing for these happen in different nodes with functions
|
|
|
|
* geo_movtex_draw_colored_no_update and geo_movtex_draw_colored_2_no_update.
|
|
|
|
* Usually the updating happens in the same function that draws it, but in
|
|
|
|
* these cases the same model has multiple instances, and you don't want the
|
|
|
|
* model to update multiple times.
|
|
|
|
* Note that the final TTC only has one big treadmill though.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
Gfx *geo_movtex_update_horizontal(s32 callContext, struct GraphNode *node, UNUSED f32 mtx[4][4]) {
|
|
|
|
void *movtexVerts;
|
|
|
|
|
|
|
|
if (callContext == GEO_CONTEXT_RENDER) {
|
|
|
|
struct GraphNodeGenerated *asGenerated = (struct GraphNodeGenerated *) node;
|
|
|
|
|
|
|
|
switch (asGenerated->parameter) {
|
|
|
|
case MOVTEX_SSL_SAND_PIT_OUTSIDE:
|
|
|
|
movtexVerts = segmented_to_virtual(ssl_movtex_tris_quicksand_pit);
|
|
|
|
break;
|
|
|
|
case MOVTEX_SSL_SAND_PIT_PYRAMID:
|
|
|
|
movtexVerts = segmented_to_virtual(ssl_movtex_tris_pyramid_quicksand_pit);
|
|
|
|
break;
|
|
|
|
case MOVTEX_TREADMILL_BIG:
|
|
|
|
movtexVerts = segmented_to_virtual(ttc_movtex_tris_big_surface_treadmill);
|
|
|
|
break;
|
|
|
|
case MOVTEX_TREADMILL_SMALL:
|
|
|
|
movtexVerts = segmented_to_virtual(ttc_movtex_tris_small_surface_treadmill);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
update_moving_texture_offset(movtexVerts, MOVTEX_ATTR_COLORED_S);
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|