Skip to content

Commit

Permalink
arrays: allow using positions and normals as texture coordinate inputs
Browse files Browse the repository at this point in the history
The GX_VA_TEX* formats only allow up to two components in the texture
input coordinates (s and t), but OpenGL applications can provide up to
four, along with a texture matrix to transform them.
While we are not able to support this general case (not easily, at
least!), we can handle the case where the array with the texture input
coordinates is the same passed for the positional or normal attributes,
because in that case we can skip re-uploading these coordinates at all
and can setup the TEV to read texture coordinates from GX_TG_POS or
GX_TG_NRM and transform them with the given matrix.

This works well with Neverball, which passes the same array of positions
as texture coordinates when it needs to apply the ball shadow to the
object lying under the ball.
  • Loading branch information
mardy committed Nov 15, 2024
1 parent 23e7ae2 commit 7c12f42
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 14 deletions.
46 changes: 42 additions & 4 deletions src/arrays.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ struct VertexReaderBase {
this->dup_color = dup_color;
}

void set_tex_coord_source(uint8_t source) { tex_coord_source = source; }
bool has_same_data(VertexReaderBase *other) const {
return data == other->data && stride == other->stride;
}

virtual void process_element(int index) = 0;

virtual void read_color(int index, GXColor *color) = 0;
Expand All @@ -174,6 +179,7 @@ struct VertexReaderBase {
const char *data;
uint16_t stride;
bool dup_color;
uint8_t tex_coord_source;
VboType vbo;
};

Expand Down Expand Up @@ -452,26 +458,53 @@ void _ogx_arrays_setup_draw(bool has_normals, uint8_t num_colors,
GX_ClearVtxDesc();
s_num_tex_arrays = 0;

get_reader(&glparamstate.vertex_array)->setup_draw();
VertexReaderBase *vertex_reader = get_reader(&glparamstate.vertex_array);
vertex_reader->setup_draw();

VertexReaderBase *normal_reader = nullptr;
if (has_normals) {
get_reader(&glparamstate.normal_array)->setup_draw();
normal_reader = get_reader(&glparamstate.normal_array);
normal_reader->setup_draw();
}
if (num_colors > 0) {
VertexReaderBase *r = get_reader(&glparamstate.color_array);
r->enable_duplicate_color(num_colors > 1);
r->setup_draw();
}

int sent_tex_arrays = 0;
s_tex_unit_mask = 0;
if (tex_unit_mask) {
for (int i = 0; i < MAX_TEXTURE_UNITS; i++) {
if (tex_unit_mask & (1 << i)) {
get_reader(&glparamstate.texcoord_array[i])->setup_draw();
VertexReaderBase *r = get_reader(&glparamstate.texcoord_array[i]);
/* See if the data array is the same as the positional or
* normal array. This is not just an optimization, it's
* actually needed because GX only supports up to two input
* coordinates for GX_VA_TEXx, but the client might provide
* three (along with an appropriate texture matrix). So, at
* least in those cases where these arrays coincide, we can
* support having three texture input coordinates. */
if (r->has_same_data(vertex_reader)) {
r->set_tex_coord_source(GX_TG_POS);
} else if (normal_reader &&
r->has_same_data(normal_reader)) {
r->set_tex_coord_source(GX_TG_NRM);
} else {
/* We could go on and check if this array has the same data
* of another texture array sent earlier in this same loop,
* but let's leave this optimisation for later. */
r->setup_draw();
r->set_tex_coord_source(GX_TG_TEX0 + sent_tex_arrays++);
s_tex_unit_mask |= (1 << i);
}
}
}
}

s_has_normals = has_normals;
s_num_colors = num_colors;
s_tex_unit_mask = tex_unit_mask;
/* s_tex_unit_mask has been set in the loop above */
}

void _ogx_arrays_process_element(int index)
Expand Down Expand Up @@ -509,6 +542,11 @@ void _ogx_array_reader_process_element(OgxArrayReader *reader, int index)
r->process_element(index);
}

uint8_t _ogx_array_reader_get_tex_coord_source(OgxArrayReader *reader)
{
return get_reader(reader)->tex_coord_source;
}

void _ogx_array_reader_read_pos3f(OgxArrayReader *reader,
int index, float *pos)
{
Expand Down
1 change: 1 addition & 0 deletions src/arrays.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ void _ogx_arrays_process_element(int index);
void _ogx_array_reader_enable_dup_color(OgxArrayReader *reader,
bool dup_color);
void _ogx_array_reader_process_element(OgxArrayReader *reader, int index);
uint8_t _ogx_array_reader_get_tex_coord_source(OgxArrayReader *reader);

void _ogx_array_reader_read_pos3f(OgxArrayReader *reader,
int index, float *pos);
Expand Down
24 changes: 14 additions & 10 deletions src/texture_unit.c
Original file line number Diff line number Diff line change
Expand Up @@ -447,10 +447,6 @@ void _ogx_setup_texture_stages(u8 raster_reg_index, u8 channel)
u8 prev_rgb = raster_rgb;
u8 prev_alpha = raster_alpha;

/* This variable holds the number of enabled texture units for which the
* client provided texture coordinates (not generated, but literally a
* GX_VA_TEX* array was specified). */
int units_with_coordinates = 0;
for (int tex = 0; tex < MAX_TEXTURE_UNITS; tex++) {
if (!(glparamstate.texture_enabled & (1 << tex))) continue;

Expand All @@ -461,7 +457,8 @@ void _ogx_setup_texture_stages(u8 raster_reg_index, u8 channel)
glparamstate.cs.texcoord_enabled & (1 << tex);
u8 input_coordinates;
if (has_texture_coordinates) {
input_coordinates = GX_TG_TEX0 + units_with_coordinates++;
input_coordinates = _ogx_array_reader_get_tex_coord_source(
&glparamstate.texcoord_array[tex]);
} else if (!tu->gen_enabled) {
warning("Skipping texture unit, since coordinates are missing.");
continue;
Expand All @@ -476,12 +473,19 @@ void _ogx_setup_texture_stages(u8 raster_reg_index, u8 channel)
prev_rgb, prev_alpha,
raster_rgb, raster_alpha, channel);

setup_texture_stage_matrix(tu, dtt_matrix);
if (tu->gen_enabled) {
setup_texture_gen(tu, tex_coord, dtt_matrix, input_coordinates);
if (input_coordinates == GX_TG_POS || input_coordinates == GX_TG_NRM) {
u8 matrix_src = GX_TEXMTX0 + _ogx_gpu_resources->texmtx_first++ * 3;
GX_LoadTexMtxImm(tu->matrix[tu->matrix_index], matrix_src, GX_MTX2x4);
GX_SetTexCoordGen(tex_coord, GX_TG_MTX2x4,
input_coordinates, matrix_src);
} else {
GX_SetTexCoordGen2(tex_coord, GX_TG_MTX2x4, input_coordinates,
GX_IDENTITY, FALSE, dtt_matrix);
setup_texture_stage_matrix(tu, dtt_matrix);
if (tu->gen_enabled) {
setup_texture_gen(tu, tex_coord, dtt_matrix, input_coordinates);
} else {
GX_SetTexCoordGen2(tex_coord, GX_TG_MTX2x4, input_coordinates,
GX_IDENTITY, FALSE, dtt_matrix);
}
}

/* All texture stages after the first one get their vertex color from
Expand Down

0 comments on commit 7c12f42

Please sign in to comment.