diff --git a/src/BRSRC13/CORE/V1DB/prepmesh.c b/src/BRSRC13/CORE/V1DB/prepmesh.c index 68b6434f..2616d884 100644 --- a/src/BRSRC13/CORE/V1DB/prepmesh.c +++ b/src/BRSRC13/CORE/V1DB/prepmesh.c @@ -20,6 +20,8 @@ pm_temp_edge* pm_edge_table; #define PREP_ALIGN(f) (f) +#define MODF_USES_DEFAULT 0x8000 + // IDA: int __usercall addEdge@(br_uint_16 first@, br_uint_16 last@) int addEdge(br_uint_16 first, br_uint_16 last) { pm_temp_edge* tep; diff --git a/src/BRSRC13/include/brender/br_defs.h b/src/BRSRC13/include/brender/br_defs.h index c105999c..5193c44f 100644 --- a/src/BRSRC13/include/brender/br_defs.h +++ b/src/BRSRC13/include/brender/br_defs.h @@ -23,6 +23,8 @@ #define BR_COLOUR_RGB(r, g, b) \ ((((unsigned int)(r)) << 16) | (((unsigned int)(g)) << 8) | ((unsigned int)(b))) +#define BR_ALPHA(c) (((c) >> 24) & 0xFF) + #define BR_ANGLE_DEG(deg) ((br_angle)((deg)*182)) #define BR_ANGLE_RAD(rad) ((br_angle)((rad)*10430)) diff --git a/src/BRSRC13/include/brender/br_types.h b/src/BRSRC13/include/brender/br_types.h index 021ce7ed..924dda37 100644 --- a/src/BRSRC13/include/brender/br_types.h +++ b/src/BRSRC13/include/brender/br_types.h @@ -2997,19 +2997,24 @@ enum { * Bits for br_model->flags */ enum { - BR_MODF_DONT_WELD = 0x0001, /* Vertices with same x,y,z cannot be merged */ - BR_MODF_KEEP_ORIGINAL = 0x0002, /* Don't release model->faces/vertices during ModelUpdate() */ - BR_MODF_GENERATE_TAGS = 0x0004, /* Allocate and fill in the face and vertex tag structures */ - BR_MODF_QUICK_UPDATE = 0x0010, /* ModelUpdate is fast - but may produce slower models */ - BR_MODF_CUSTOM = 0x0020, /* Invoke custom callback for this model */ - BR_MODF_PREPREPARED = 0x0040, /* Model structure is pre-prepared - update performs no work */ - BR_MODF_UPDATEABLE = 0x0080, /* Added by Jeff from Errols code */ - BR_MODF_CREASE = 0x0100, /* Create creases in smoothing along edges if face<->face angle is g.t model->crease */ - BR_MODF_CUSTOM_NORMALS = 0x0200, /* Uses vertex normals from br_vertex structure */ - BR_MODF_CUSTOM_BOUNDS = 0x0400, /* Bounding box is already set up */ - // BR_MODF_FACES_ONLY = 0x0800, /* Model will only be used to render faces (not edges or points) */ + BR_MODF_DONT_WELD = 0x0001, /* Vertices with same x,y,z cannot be merged */ + BR_MODF_KEEP_ORIGINAL = 0x0002, /* Don't release model->faces/vertices during ModelUpdate() */ + BR_MODF_GENERATE_TAGS = 0x0004, /* Allocate and fill in the face and vertex tag structures */ + BR_MODF_QUICK_UPDATE = 0x0010, /* ModelUpdate is fast - but may produce slower models */ + BR_MODF_CUSTOM = 0x0020, /* Invoke custom callback for this model */ + BR_MODF_PREPREPARED = 0x0040, /* Model structure is pre-prepared - update performs no work */ + BR_MODF_UPDATEABLE = 0x0080, /* Added by Jeff from Errols code */ + BR_MODF_CREASE = 0x0100, /* Create creases in smoothing along edges if face<->face angle is g.t model->crease */ + BR_MODF_CUSTOM_NORMALS = 0x0200, /* Uses vertex normals from br_vertex structure */ + BR_MODF_CUSTOM_BOUNDS = 0x0400, /* Bounding box is already set up */ + BR_MODF_FACES_ONLY = 0x0800, /* Model will only be used to render faces (not edges or points) */ + BR_MODF_USED_PREPARED_USER = 0x1000, /* User fields in prepared data used */ + BR_MODF_CUSTOM_EQUATIONS = 0x2000, /* Uses face equations from br_face structure */ + _BR_MODF_RESERVED = 0x8000, + + // Added by dethrace to force the vertex data to be reuploaded to GPU + BR_MODF_DETHRACE_FORCE_BUFFER_UPDATE = 0x4000 - MODF_USES_DEFAULT = 0x8000 }; enum { diff --git a/src/DETHRACE/common/piping.c b/src/DETHRACE/common/piping.c index 2d5db318..52796162 100644 --- a/src/DETHRACE/common/piping.c +++ b/src/DETHRACE/common/piping.c @@ -12,9 +12,9 @@ #include "pedestrn.h" #include "replay.h" #include "skidmark.h" +#include "sound.h" #include "spark.h" #include "sys.h" -#include "sound.h" #include "utility.h" #include "world.h" #include @@ -88,7 +88,7 @@ tU8* gPipe_buffer_oldest; tU32 gPipe_buffer_size; tU8* gLocal_buffer; tU32 gLocal_buffer_size; -tPipe_chunk *gIncidentChunk; // FIXME: added by DethRace (really needed?) +tPipe_chunk* gIncidentChunk; // FIXME: added by DethRace (really needed?) #define LOCAL_BUFFER_SIZE 15000 @@ -153,8 +153,7 @@ int IsActionReplayAvailable() { int SomeReplayLeft() { LOG_TRACE("()"); - return ((GetReplayDirection() >= 1 && gPipe_play_ptr != gPipe_record_ptr) || - (GetReplayDirection() <= -1 && gPipe_play_ptr != gPipe_buffer_oldest)); + return ((GetReplayDirection() >= 1 && gPipe_play_ptr != gPipe_record_ptr) || (GetReplayDirection() <= -1 && gPipe_play_ptr != gPipe_buffer_oldest)); } // IDA: void __cdecl DisablePipedSounds() @@ -179,7 +178,7 @@ tU32 LengthOfSession(tPipe_session* pSession) { LOG_TRACE("(%p)", pSession); #define SIZEOF_CHUNK(MEMBER) (offsetof(tPipe_chunk, chunk_data) + sizeof(pSession->chunks.chunk_data.MEMBER)) -#define ROUND_UP(V, M) (((V) + (M) - 1) & (~((M) - 1))) +#define ROUND_UP(V, M) (((V) + (M)-1) & (~((M)-1))) switch (pSession->chunk_type) { case ePipe_chunk_actor_rstyle: @@ -1551,12 +1550,12 @@ int ApplyPipedSession(tU8** pPtr) { if (*pPtr == gPipe_record_ptr) { return 1; } - gEnd_of_session = *pPtr + (LengthOfSession((tPipe_session *)*pPtr) - sizeof(tU16)); - REPLAY_DEBUG_ASSERT(((tPipe_session *)*pPtr)->magic1 == REPLAY_DEBUG_MAGIC1); - chunk_ptr = (tPipe_chunk *)(*pPtr + offsetof(tPipe_session, chunks)); + gEnd_of_session = *pPtr + (LengthOfSession((tPipe_session*)*pPtr) - sizeof(tU16)); + REPLAY_DEBUG_ASSERT(((tPipe_session*)*pPtr)->magic1 == REPLAY_DEBUG_MAGIC1); + chunk_ptr = (tPipe_chunk*)(*pPtr + offsetof(tPipe_session, chunks)); return_value = 0; - chunk_type = ((tPipe_session *)*pPtr)->chunk_type; - for (i = 0; i < ((tPipe_session *)*pPtr)->number_of_chunks; i++) { + chunk_type = ((tPipe_session*)*pPtr)->chunk_type; + for (i = 0; i < ((tPipe_session*)*pPtr)->number_of_chunks; i++) { switch (chunk_type) { case ePipe_chunk_model_geometry: ApplyModelGeometry(&chunk_ptr); @@ -1630,9 +1629,9 @@ int ApplyPipedSession(tU8** pPtr) { } } #if defined(DETHRACE_FIX_BUGS) - *pPtr += PIPE_ALIGN(LengthOfSession((tPipe_session *)*pPtr)); + *pPtr += PIPE_ALIGN(LengthOfSession((tPipe_session*)*pPtr)); #else - *pPtr += LengthOfSession((tPipe_session *)*pPtr); + *pPtr += LengthOfSession((tPipe_session*)*pPtr); #endif if (*pPtr >= gPipe_buffer_working_end && *pPtr != gPipe_record_ptr) { *pPtr = gPipe_buffer_start; @@ -1749,8 +1748,7 @@ void UndoPedestrian(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) { temp_prev_chunk = pPrev_chunk; if (pPrev_chunk == NULL) { ApplyPedestrian(pChunk); - } - else { + } else { gDisable_advance = 1; ApplyPedestrian(&temp_prev_chunk); gDisable_advance = 0; @@ -1775,8 +1773,7 @@ void UndoCar(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) { temp_prev_chunk = pPrev_chunk; if (pPrev_chunk == NULL) { ApplyCar(pChunk); - } - else { + } else { gDisable_advance = 1; ApplyCar(&temp_prev_chunk); gDisable_advance = 0; @@ -1788,7 +1785,7 @@ void UndoCar(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) { void UndoSound(tPipe_chunk** pChunk) { LOG_TRACE("(%p)", pChunk); - AdvanceChunkPtr(pChunk,ePipe_chunk_sound); + AdvanceChunkPtr(pChunk, ePipe_chunk_sound); } // IDA: void __usercall UndoDamage(tPipe_chunk **pChunk@) @@ -1799,8 +1796,7 @@ void UndoDamage(tPipe_chunk** pChunk) { if (((*pChunk)->subject_index & 0xff00) == 0) { car = &gProgram_state.current_car; - } - else { + } else { car = GetCarSpec((*pChunk)->subject_index >> 8, (*pChunk)->subject_index & 0xff); } for (i = 0; i < COUNT_OF(car->damage_units); i++) { @@ -1885,8 +1881,7 @@ void UndoScreenWobble(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) { gDisable_advance = 1; if (pPrev_chunk == NULL) { SetScreenWobble(0, 0); - } - else { + } else { ApplyScreenWobble(&temp_prev_chunk); } gDisable_advance = 0; @@ -1959,8 +1954,7 @@ void UndoSplash(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) { gDisable_advance = 1; if (pPrev_chunk == NULL) { ((((*pChunk)->subject_index & 0xff00) == 0) ? &gProgram_state.current_car : GetCarSpec((*pChunk)->subject_index >> 8, (*pChunk)->subject_index & 0xff))->water_d = 10000.f; - } - else { + } else { ApplySplash(&temp_prev_chunk); } gDisable_advance = 0; @@ -1995,8 +1989,7 @@ void UndoSkidAdjustment(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) { gDisable_advance = 1; if (pPrev_chunk == NULL) { HideSkid((*pChunk)->subject_index); - } - else { + } else { ApplySkidAdjustment(&pPrev_chunk); } gDisable_advance = 0; @@ -2112,7 +2105,7 @@ tU32 FindPrevFrameTime(tU8* pPtr) { temp_ptr = pPtr; do { if (MoveSessionPointerBackOne(&temp_ptr)) { - return 0; + return 0; } } while (((tPipe_session*)temp_ptr)->chunk_type != ePipe_chunk_frame_boundary); return ((tPipe_session*)temp_ptr)->chunks.chunk_data.frame_boundary_data.time; @@ -2317,9 +2310,9 @@ tU32 GetARStartTime() { temp_ptr = gPipe_buffer_oldest; do { - if (MoveSessionPointerForwardOne(&temp_ptr)) { - return 0; - } + if (MoveSessionPointerForwardOne(&temp_ptr)) { + return 0; + } } while (((tPipe_session*)temp_ptr)->chunk_type != ePipe_chunk_frame_boundary); return ((tPipe_session*)temp_ptr)->chunks.chunk_data.frame_boundary_data.time; } diff --git a/src/DETHRACE/common/spark.c b/src/DETHRACE/common/spark.c index 32ed8e19..afbb472d 100644 --- a/src/DETHRACE/common/spark.c +++ b/src/DETHRACE/common/spark.c @@ -6,6 +6,7 @@ #include "globvars.h" #include "globvrkm.h" #include "graphics.h" +#include "harness/hooks.h" #include "harness/trace.h" #include "loading.h" #include "opponent.h" @@ -874,8 +875,7 @@ void SmokeLine(int l, int x, br_scalar zbuff, int r_squared, tU8* scr_ptr, tU16* for (i = 0; i < l; i++) { if (*depth_ptr > z) { - offset = ((shade_offset_int - r_squared * r_multiplier_int) >> 8) & - 0xffffff00; + offset = ((shade_offset_int - r_squared * r_multiplier_int) >> 8) & 0xffffff00; #if defined(DETHRACE_FIX_BUGS) /* Prevent buffer underflows by capping negative offsets. */ offset = MAX(0, offset); @@ -2051,84 +2051,92 @@ void SmudgeCar(tCar_spec* pCar, int fire_point) { if (gAusterity_mode) { return; } + + v = fire_point; + group = 0; actor = pCar->car_model_actors[pCar->principal_car_actor].actor; model = actor->model; bonny = pCar->car_model_actors[pCar->car_actor_count - 1].actor; n = 0; - j = 0; + real_vertex_number = 0; if ((model->flags & BR_MODF_KEEP_ORIGINAL) != 0 || (model->flags & BR_MODF_UPDATEABLE) != 0) { - BrVector3Copy(&bonny_pos, &V11MODEL(model)->groups->vertices[fire_point].p); + point = V11MODEL(model)->groups[group].vertices[fire_point].p; StartPipingSession(ePipe_chunk_smudge); for (group = 0; group < V11MODEL(model)->ngroups; group++) { - for (v = 0; v < V11MODEL(model)->groups[group].nvertices; v++) { - BrVector3Sub(&tv, &V11MODEL(model)->groups[group].vertices[v].p, &bonny_pos); + for (j = 0; j < V11MODEL(model)->groups[group].nvertices; j++) { + BrVector3Sub(&tv, &V11MODEL(model)->groups[group].vertices[j].p, &point); ts = (.0144f - BrVector3LengthSquared(&tv) / SRandomBetween(.5f, 1.f)) / .0144f * 127.f; if (ts > 0.f) { - ts += V11MODEL(model)->groups[group].vertex_colours[v] >> 24; + ts += BR_ALPHA(V11MODEL(model)->groups[group].vertex_colours[j]); if (ts > 255.f) { ts = 255.f; } - real_vertex_number = ts; - if (V11MODEL(model)->groups[group].vertex_colours[v] >> 24 != real_vertex_number) { - data[n].vertex_index = j; - data[n].light_index = real_vertex_number - (V11MODEL(model)->groups[group].vertex_colours[v] >> 24); - V11MODEL(model)->groups[group].vertex_colours[v] = real_vertex_number << 24; + if (BR_ALPHA(V11MODEL(model)->groups[group].vertex_colours[j]) != (int)ts) { + data[n].vertex_index = real_vertex_number; + data[n].light_index = (int)ts - BR_ALPHA(V11MODEL(model)->groups[group].vertex_colours[j]); + V11MODEL(model)->groups[group].vertex_colours[j] = (int)ts << 24; if ((model->flags & BR_MODF_UPDATEABLE) != 0) { - model->vertices[V11MODEL(model)->groups[group].vertex_user[v]].index = real_vertex_number; + model->vertices[V11MODEL(model)->groups[group].vertex_user[j]].index = (int)ts; } - n += 1; + n++; if (n >= COUNT_OF(data)) { break; } } } - j = j + 1; + real_vertex_number++; } if (n >= COUNT_OF(data)) { break; } } - if (n != 0) { + if (n > 0) { AddSmudgeToPipingSession(pCar->car_ID, pCar->principal_car_actor, n, data); + // Added by dethrace to update gpu-buffered vertices + model->flags |= BR_MODF_DETHRACE_FORCE_BUFFER_UPDATE; } + n = 0; - j = 0; + real_vertex_number = 0; if (actor != bonny) { b_model = bonny->model; - BrVector3Add(&tv, &actor->t.t.translate.t, &bonny_pos); - BrVector3Sub(&tv, &tv, &bonny->t.t.translate.t); - BrMatrix34TApplyV(&point, &tv, &bonny->t.t.mat); + BrVector3Add(&tv, &actor->t.t.translate.t, &point); + BrVector3Accumulate(&tv, &bonny->t.t.translate.t); + BrMatrix34TApplyV(&bonny_pos, &tv, &bonny->t.t.mat); for (group = 0; group < V11MODEL(b_model)->ngroups; group++) { - for (v = 0; v < V11MODEL(b_model)->groups[group].nvertices; v++) { - BrVector3Sub(&tv, &V11MODEL(b_model)->groups[group].vertices[v].p, &point); - ts = (.0144f - BrVector3LengthSquared(&tv)) / SRandomBetween(.5f, 1.f) / .0144f * 127.f; + j = 0; + for (j = 0; j < V11MODEL(b_model)->groups[group].nvertices; j++) { + BrVector3Sub(&tv, &V11MODEL(b_model)->groups[group].vertices[j].p, &bonny_pos); + ts = (.0144f - BrVector3LengthSquared(&tv) / SRandomBetween(.5f, 1.f)) / .0144f * 127.f; if (ts > 0.f) { - ts += V11MODEL(b_model)->groups[group].vertex_colours[v] >> 24; + ts += BR_ALPHA(V11MODEL(b_model)->groups[group].vertex_colours[j]); if (ts > 255.f) { ts = 255.f; } - real_vertex_number = ts; - if (V11MODEL(b_model)->groups[group].vertex_colours[v] >> 24 != real_vertex_number) { - data[n].vertex_index = j; - data[n].light_index = real_vertex_number - (V11MODEL(b_model)->groups[group].vertex_colours[v] >> 24); - V11MODEL(b_model)->groups[group].vertex_colours[v] = real_vertex_number << 24; + if (BR_ALPHA(V11MODEL(b_model)->groups[group].vertex_colours[j]) != (int)ts) { + data[n].vertex_index = real_vertex_number; + data[n].light_index = (int)ts - BR_ALPHA(V11MODEL(b_model)->groups[group].vertex_colours[j]); + V11MODEL(b_model)->groups[group].vertex_colours[j] = (int)ts << 24; if ((b_model->flags & BR_MODF_UPDATEABLE) != 0) { - b_model->vertices[V11MODEL(b_model)->groups[group].vertex_user[v]].index = real_vertex_number; + b_model->vertices[V11MODEL(b_model)->groups[group].vertex_user[j]].index = (int)ts; } - n += 1; - if (n > COUNT_OF(data)) { + n++; + if (n >= COUNT_OF(data)) { break; } } } - j += 1; + real_vertex_number++; } if (n >= COUNT_OF(data)) { break; } } - if (n != 0) { + if (n > 0) { AddSmudgeToPipingSession(pCar->car_ID, pCar->car_actor_count - 1, n, data); + + // Added by dethrace to update gpu-buffered vertices + b_model->flags |= BR_MODF_DETHRACE_FORCE_BUFFER_UPDATE; } } EndPipingSession(); diff --git a/src/harness/harness.c b/src/harness/harness.c index 889e563c..4bf1d440 100644 --- a/src/harness/harness.c +++ b/src/harness/harness.c @@ -381,7 +381,7 @@ void Harness_Hook_BrZbSceneRenderBegin(br_actor* world, br_actor* camera, br_pix } void Harness_Hook_renderActor(br_actor* actor, br_model* model, br_material* material, br_token type) { - renderer->Model(actor, model, renderer_state->state.matrix.model_to_view, type); + renderer->Model(actor, model, material, type, renderer_state->state.matrix.model_to_view); } void Harness_Hook_BrZbSceneRenderEnd() { @@ -444,14 +444,6 @@ void Harness_Hook_GetMouseButtons(int* pButton1, int* pButton2) { IOPlatform_GetMouseButtons(pButton1, pButton2); } -// Sound hooks -void Harness_Hook_S3Service(int unk1, int unk2) { - Sound_Service(); -} - -void Harness_Hook_S3StopAllOutletSounds() { -} - // Filesystem hooks FILE* Harness_Hook_fopen(const char* pathname, const char* mode) { return OS_fopen(pathname, mode); diff --git a/src/harness/renderers/gl/gl_renderer.c b/src/harness/renderers/gl/gl_renderer.c index 8521c32b..4e631415 100644 --- a/src/harness/renderers/gl/gl_renderer.c +++ b/src/harness/renderers/gl/gl_renderer.c @@ -41,19 +41,31 @@ static br_pixelmap* current_shade_table; // If the counters are equal, we can avoid re-uploading the same thing. static unsigned int flush_counter = 0, colourbuffer_upload_counter = 0; +typedef struct gl_vertex { + br_vector3 p; + br_vector2 map; + br_vector3 n; + float colour_index; // float to allow interpolation +} gl_vertex; + struct { - GLuint texture_pixelmap; - GLuint uv_transform; - GLuint shade_table; - GLuint blend_table; - GLuint blend_enabled; - GLuint colour_buffer_texture; GLuint model, view, projection; - GLuint palette_index_override; GLuint clip_plane_count; GLuint clip_planes[6]; - GLuint light_value; + GLuint colour_buffer_texture; GLuint viewport_height; + + GLuint material_flags; + GLuint material_texture_enabled; + GLuint material_texture_pixelmap; + GLuint material_uv_transform; + GLuint material_shade_table; + GLuint material_blend_enabled; + GLuint material_blend_table; + GLuint material_index_base; + GLuint material_index_range; + GLuint material_shade_table_height; + } uniforms_3d; struct { @@ -130,23 +142,29 @@ void LoadShaders() { sprintf(name, "u_clip_planes[%d]", i); uniforms_3d.clip_planes[i] = GetValidatedUniformLocation(shader_program_3d, name); } + uniforms_3d.model = GetValidatedUniformLocation(shader_program_3d, "u_model"); - uniforms_3d.texture_pixelmap = GetValidatedUniformLocation(shader_program_3d, "u_texture_pixelmap"); - uniforms_3d.uv_transform = GetValidatedUniformLocation(shader_program_3d, "u_texture_coords_transform"); - uniforms_3d.shade_table = GetValidatedUniformLocation(shader_program_3d, "u_shade_table"); - uniforms_3d.blend_table = GetValidatedUniformLocation(shader_program_3d, "u_blend_table"); - uniforms_3d.blend_enabled = GetValidatedUniformLocation(shader_program_3d, "u_blend_enabled"); uniforms_3d.colour_buffer_texture = GetValidatedUniformLocation(shader_program_3d, "u_colour_buffer"); uniforms_3d.projection = GetValidatedUniformLocation(shader_program_3d, "u_projection"); - uniforms_3d.palette_index_override = GetValidatedUniformLocation(shader_program_3d, "u_palette_index_override"); uniforms_3d.view = GetValidatedUniformLocation(shader_program_3d, "u_view"); - uniforms_3d.light_value = GetValidatedUniformLocation(shader_program_3d, "u_light_value"); uniforms_3d.viewport_height = GetValidatedUniformLocation(shader_program_3d, "u_viewport_height"); + uniforms_3d.material_flags = GetValidatedUniformLocation(shader_program_3d, "u_material_flags"); + uniforms_3d.material_texture_enabled = GetValidatedUniformLocation(shader_program_3d, "u_material_texture_enabled"); + uniforms_3d.material_texture_pixelmap = GetValidatedUniformLocation(shader_program_3d, "u_material_texture_pixelmap"); + uniforms_3d.material_uv_transform = GetValidatedUniformLocation(shader_program_3d, "u_material_uv_transform"); + uniforms_3d.material_shade_table = GetValidatedUniformLocation(shader_program_3d, "u_material_shade_table"); + uniforms_3d.material_shade_table_height = GetValidatedUniformLocation(shader_program_3d, "u_material_shade_table_height"); + uniforms_3d.material_blend_enabled = GetValidatedUniformLocation(shader_program_3d, "u_material_blend_enabled"); + uniforms_3d.material_blend_table = GetValidatedUniformLocation(shader_program_3d, "u_material_blend_table"); + uniforms_3d.material_index_base = GetValidatedUniformLocation(shader_program_3d, "u_material_index_base"); + uniforms_3d.material_index_range = GetValidatedUniformLocation(shader_program_3d, "u_material_index_range"); + // bind the uniform samplers to texture units - glUniform1i(uniforms_3d.texture_pixelmap, 0); - glUniform1i(uniforms_3d.shade_table, 2); - glUniform1i(uniforms_3d.blend_table, 3); + glUniform1i(uniforms_3d.material_texture_pixelmap, 0); + // palette occupies texture unit 1 but not required during 3d rendering + glUniform1i(uniforms_3d.material_shade_table, 2); + glUniform1i(uniforms_3d.material_blend_table, 3); glUniform1i(uniforms_3d.colour_buffer_texture, 4); } @@ -347,7 +365,7 @@ void GLRenderer_BeginScene(br_actor* camera, br_pixelmap* colour_buffer, br_pixe last_depth_buffer = depth_buffer; glViewport(colour_buffer->base_x, render_height - colour_buffer->height - colour_buffer->base_y, colour_buffer->width, colour_buffer->height); glUseProgram(shader_program_3d); - glUniform1i(uniforms_3d.viewport_height, render_height); + glUniform1ui(uniforms_3d.viewport_height, render_height); current_material = NULL; current_shade_table = NULL; @@ -470,7 +488,7 @@ void GLRenderer_BufferModel(br_model* model) { } } - fmt_vertex* verts = malloc(sizeof(fmt_vertex) * total_verts); + gl_vertex* verts = malloc(sizeof(gl_vertex) * total_verts); unsigned int* indices = malloc(sizeof(int) * 3 * total_faces); int v_index = 0; @@ -482,6 +500,7 @@ void GLRenderer_BufferModel(br_model* model) { verts[v_index].p = v->p; verts[v_index].n = v->n; verts[v_index].map = v->map; + verts[v_index].colour_index = BR_ALPHA(v11->groups[g].vertex_colours[i]); v_index++; } for (int i = 0; i < v11->groups[g].nfaces; i++) { @@ -500,13 +519,19 @@ void GLRenderer_BufferModel(br_model* model) { // Vertices glBindVertexArray(ctx->vao_id); glBindBuffer(GL_ARRAY_BUFFER, ctx->vbo_id); - glBufferData(GL_ARRAY_BUFFER, sizeof(fmt_vertex) * total_verts, verts, GL_STATIC_DRAW); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(fmt_vertex), (void*)offsetof(fmt_vertex, p)); + glBufferData(GL_ARRAY_BUFFER, sizeof(gl_vertex) * total_verts, verts, GL_STATIC_DRAW); + // pos + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(gl_vertex), (void*)offsetof(gl_vertex, p)); glEnableVertexAttribArray(0); - glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(fmt_vertex), (void*)offsetof(fmt_vertex, n)); + // normal + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(gl_vertex), (void*)offsetof(gl_vertex, n)); glEnableVertexAttribArray(1); - glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(fmt_vertex), (void*)offsetof(fmt_vertex, map)); + // uv coordinates + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(gl_vertex), (void*)offsetof(gl_vertex, map)); glEnableVertexAttribArray(2); + // color + glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, sizeof(gl_vertex), (void*)offsetof(gl_vertex, colour_index)); + glEnableVertexAttribArray(3); // Indices glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ctx->ebo_id); @@ -526,28 +551,39 @@ void setActiveMaterial(tStored_material* material) { return; } - glUniformMatrix2x3fv(uniforms_3d.uv_transform, 1, GL_TRUE, &material->map_transform.m[0][0]); - glUniform1i(uniforms_3d.palette_index_override, material->index_base); + glUniformMatrix2x3fv(uniforms_3d.material_uv_transform, 1, GL_TRUE, &material->map_transform.m[0][0]); + + if (material->pixelmap) { + tStored_pixelmap* stored_px = material->pixelmap->stored; + if (stored_px == NULL) { + LOG_PANIC("stored_px is null for pixelmap %s", material->pixelmap->identifier); + } + glBindTexture(GL_TEXTURE_2D, stored_px->id); + glUniform1ui(uniforms_3d.material_texture_enabled, 1); + } else { + glUniform1ui(uniforms_3d.material_texture_enabled, 0); + + // index_base and index_range are only used for untextured materials + glUniform1ui(uniforms_3d.material_index_base, material->index_base); + glUniform1ui(uniforms_3d.material_index_range, material->index_range); + } + if (material->shade_table) { + glUniform1ui(uniforms_3d.material_shade_table_height, material->shade_table->height); GLRenderer_SetShadeTable(material->shade_table); } if (material->index_blend) { - glUniform1i(uniforms_3d.blend_enabled, 1); + glUniform1ui(uniforms_3d.material_blend_enabled, 1); GLRenderer_SetBlendTable(material->index_blend); // materials with index_blend do not write to depth buffer (https://www.cwaboard.co.uk/viewtopic.php?p=105846&sid=58ad8910238000ca14b01dad85117175#p105846) glDepthMask(GL_FALSE); } else { - glUniform1i(uniforms_3d.blend_enabled, 0); + glUniform1ui(uniforms_3d.material_blend_enabled, 0); glDepthMask(GL_TRUE); } - if ((material->flags & BR_MATF_LIGHT) && !(material->flags & BR_MATF_PRELIT) && material->shade_table) { - // TODO: light value shouldn't always be 0? Works for shadows, not sure about other things. - glUniform1i(uniforms_3d.light_value, 0); - } else { - glUniform1i(uniforms_3d.light_value, -1); - } + glUniform1ui(uniforms_3d.material_flags, material->flags); if (material->flags & (BR_MATF_TWO_SIDED | BR_MATF_ALWAYS_VISIBLE)) { glDisable(GL_CULL_FACE); @@ -555,21 +591,26 @@ void setActiveMaterial(tStored_material* material) { glEnable(GL_CULL_FACE); } - if (material->pixelmap) { - tStored_pixelmap* stored_px = material->pixelmap->stored; - if (stored_px != NULL) { - glBindTexture(GL_TEXTURE_2D, stored_px->id); - glUniform1i(uniforms_3d.palette_index_override, -1); - } - } + CHECK_GL_ERROR("GLRenderer_RenderModelxx"); } -void GLRenderer_Model(br_actor* actor, br_model* model, br_matrix34 model_matrix, br_token render_type) { +void GLRenderer_Model(br_actor* actor, br_model* model, br_material* material, br_token render_type, br_matrix34 model_matrix) { tStored_model_context* ctx; + v11group* group; + int element_index = 0; + + if (model->flags & BR_MODF_DETHRACE_FORCE_BUFFER_UPDATE) { + if (model->stored) { + ((br_object*)model->stored)->dispatch->_free((br_object*)model->stored); + model->stored = NULL; + } + GLRenderer_BufferModel(model); + model->flags &= ~BR_MODF_DETHRACE_FORCE_BUFFER_UPDATE; + } + ctx = model->stored; v11model* v11 = model->prepared; - // LOG_DEBUG("model rendering %s", model->identifier); if (v11 == NULL) { // LOG_WARN("No model prepared for %s", model->identifier); return; @@ -586,15 +627,8 @@ void GLRenderer_Model(br_actor* actor, br_model* model, br_matrix34 model_matrix glBindVertexArray(ctx->vao_id); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ctx->ebo_id); - // br_actor can have a material too, which is applied to the faces if the face doesn't have a texture - if (actor->material) { - setActiveMaterial(actor->material->stored); - } else { - // TODO: set defaults for now. This fixes missing curb materials but probably isn't the right fix. - LOG_WARN_ONCE("set default palette override for missing actor material") - glUniform1i(uniforms_3d.palette_index_override, 227); - glUniform1i(uniforms_3d.light_value, -1); - } + // set default material for this actor/model + setActiveMaterial(material->stored); switch (render_type) { case BRT_TRIANGLE: @@ -602,15 +636,13 @@ void GLRenderer_Model(br_actor* actor, br_model* model, br_matrix34 model_matrix break; case BRT_LINE: glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - glUniform1i(uniforms_3d.palette_index_override, 255); - glUniform1i(uniforms_3d.light_value, -1); + glUniform1ui(uniforms_3d.material_index_base, 255); + glUniform1ui(uniforms_3d.material_flags, 0); break; default: LOG_PANIC("render_type %d is not supported?!", render_type); } - v11group* group; - int element_index = 0; for (int g = 0; g < v11->ngroups; g++) { group = &v11->groups[g]; setActiveMaterial(group->stored); @@ -639,6 +671,7 @@ void GLRenderer_BufferMaterial(br_material* mat) { stored->flags = mat->flags; stored->shade_table = mat->index_shade; stored->index_base = mat->index_base; + stored->index_range = mat->index_range; stored->index_blend = mat->index_blend; } @@ -705,7 +738,8 @@ void GLRenderer_FlushBuffers(tRenderer_flush_type flush_type) { for (int y = 0; y < last_colour_buffer->height; y++) { dest_y--; for (int x = 0; x < last_colour_buffer->width; x++) { - depth_pixels[dest_y * render_width + x] = depth_buffer_flip_pixels[src_y * render_width + last_colour_buffer->base_x + x]; + uint16_t new_depth = depth_buffer_flip_pixels[src_y * render_width + last_colour_buffer->base_x + x]; + depth_pixels[dest_y * render_width + x] = new_depth; } src_y++; } diff --git a/src/harness/renderers/gl/gl_renderer.h b/src/harness/renderers/gl/gl_renderer.h index e4def13f..a4bb994b 100644 --- a/src/harness/renderers/gl/gl_renderer.h +++ b/src/harness/renderers/gl/gl_renderer.h @@ -47,7 +47,7 @@ void GLRenderer_SetPalette(uint8_t* rgba_colors); void GLRenderer_BeginScene(br_actor* camera, br_pixelmap* colour_buffer, br_pixelmap* depth_buffer); void GLRenderer_EndScene(); void GLRenderer_FullScreenQuad(uint8_t* screen_buffer); -void GLRenderer_Model(br_actor* actor, br_model* model, br_matrix34 model_matrix, br_token render_type); +void GLRenderer_Model(br_actor* actor, br_model* model, br_material* material, br_token render_type, br_matrix34 model_matrix); void GLRenderer_BufferTexture(br_pixelmap* pm); void GLRenderer_BufferMaterial(br_material* mat); void GLRenderer_BufferModel(br_model* model); diff --git a/src/harness/renderers/gl/stored_context.h b/src/harness/renderers/gl/stored_context.h index ffc6a50c..5b7d7ba6 100644 --- a/src/harness/renderers/gl/stored_context.h +++ b/src/harness/renderers/gl/stored_context.h @@ -15,6 +15,7 @@ typedef struct tStored_pixelmap { typedef struct tStored_material { int index_base; + int index_range; br_uint_32 flags; br_pixelmap* shade_table; br_pixelmap* pixelmap; diff --git a/src/harness/renderers/null.h b/src/harness/renderers/null.h index df38d40e..4ec360cc 100644 --- a/src/harness/renderers/null.h +++ b/src/harness/renderers/null.h @@ -5,7 +5,7 @@ void Null_BeginFrame(br_actor* camera, br_pixelmap* colour_buffer, br_pixelmap* void Null_EndFrame() {} void Null_SetPalette(uint8_t* palette) {} void Null_RenderFullScreenQuad(uint8_t* src) {} -void Null_RenderModel(br_actor* actor, br_model* model, br_matrix34 model_matrix, br_token render_type) {} +void Null_RenderModel(br_actor* actor, br_model* model, br_material* material, br_token render_type, br_matrix34 model_matrix) {} void Null_RenderFrameBuffer() {} void Null_ClearBuffers() {} void Null_BufferTexture(br_pixelmap* pm) {} diff --git a/src/harness/renderers/renderer.h b/src/harness/renderers/renderer.h index ca5c1dfb..6229b617 100644 --- a/src/harness/renderers/renderer.h +++ b/src/harness/renderers/renderer.h @@ -14,7 +14,7 @@ typedef struct tRenderer { void (*EndScene)(); void (*SetPalette)(uint8_t* palette); void (*FullScreenQuad)(uint8_t* src); - void (*Model)(br_actor* actor, br_model* model, br_matrix34 model_matrix, br_token render_type); + void (*Model)(br_actor* actor, br_model* model, br_material* material, br_token render_type, br_matrix34 model_matrix); void (*ClearBuffers)(); void (*BufferTexture)(br_pixelmap* pm); void (*BufferMaterial)(br_material* mat); diff --git a/src/harness/resources/3d_frag.glsl b/src/harness/resources/3d_frag.glsl index 51fe54af..c6e3b3a5 100644 --- a/src/harness/resources/3d_frag.glsl +++ b/src/harness/resources/3d_frag.glsl @@ -1,58 +1,108 @@ #version 140 #extension GL_ARB_explicit_attrib_location : require +// Input, output variables +// ======================= + in vec3 v_frag_pos; in vec3 v_normal; in vec2 v_tex_coord; +// this is actually an integer palette index. We define it as a float to allow opengl to +// interpolate the value across each face. +in float v_color; + +// 256 color mode, so each calculated color is a palette index. out uint out_palette_index; -uniform mat2x3 u_texture_coords_transform; -uniform usampler2D u_texture_pixelmap; -uniform usampler2D u_shade_table; -uniform usampler2D u_blend_table; +// Uniform variables +// ================= + +// colour_buffer is an image which contains the current content of the framebuffer uniform usampler2D u_colour_buffer; -uniform int u_palette_index_override = -1; -uniform int u_light_value = -1; +uniform uint u_viewport_height; + uniform vec4 u_clip_planes[6]; uniform int u_clip_plane_count = 0; -uniform int u_blend_enabled = 0; -uniform int u_viewport_height; + +uniform uint u_material_flags; +uniform mat2x3 u_material_uv_transform; +uniform usampler2D u_material_texture_pixelmap; +uniform uint u_material_texture_enabled; + +// material_blend_table is a 256x256 image which encodes 256 values of blending between new color and current color in framebuffer +uniform uint u_material_blend_enabled = 0u; +uniform usampler2D u_material_blend_table; + +// material_shade_table is a 256px-wide image which encodes material_shade_table_height lit shades for each color +uniform usampler2D u_material_shade_table; + +// how many lit shades are in the material_shade_table +uniform uint u_material_shade_table_height; + +// For non-textured materials, defines the starting palette index +uniform uint u_material_index_base; +// For non-textured materials, defines the range of lit shades +uniform uint u_material_index_range; + +// material_flags values +const uint BR_MATF_LIGHT = 1u; +const uint BR_MATF_PRELIT = 2u; void main() { for(int i = 0; i < u_clip_plane_count; i++) { // calculate signed plane-vertex distance - vec4 v4 = vec4(v_frag_pos.x, v_frag_pos.y, v_frag_pos.z, 1); + vec4 v4 = vec4(v_frag_pos.xyz, 1); float d = dot(u_clip_planes[i], v4); - if (d < 0.0) discard; + if (d < 0) { + discard; + } } - if (u_palette_index_override >= 0) { + if (u_material_texture_enabled == 0u) { // force palette index, no texture lookup - out_palette_index = uint(u_palette_index_override); - } else { + out_palette_index = u_material_index_base; + + if ((u_material_flags & BR_MATF_LIGHT) != 0u) { + // TODO: lighting calculations based on https://rr2000.cwaboard.co.uk/R4/BRENDER/TEBK_43.HTM#0 + uint range = u_material_index_range; + } + } + else { // calculate texture uv coordinates - vec2 sample_coord = vec3(v_tex_coord.xy, 1) * u_texture_coords_transform; - uint texel = texture(u_texture_pixelmap, sample_coord.xy).r; - if (u_light_value >= 0) { - // shade_table is a 256x256 image which encodes 256 lit shades for each color - out_palette_index = texelFetch(u_shade_table, ivec2(texel, u_light_value), 0).r; + vec2 sample_coord = vec3(v_tex_coord.xy, 1) * u_material_uv_transform; + uint texel = texture(u_material_texture_pixelmap, sample_coord.xy).r; + + // color 0 is always transparent + if (texel == 0u) { + discard; + } + + if ((u_material_flags & BR_MATF_LIGHT) != 0u) { + if ((u_material_flags & BR_MATF_PRELIT) != 0u) { + // BR_MATF_PRELIT means the light value comes from the vertex color attribute + uint calculated_lit_value = uint(v_color) / u_material_shade_table_height; + out_palette_index = texelFetch(u_material_shade_table, ivec2(texel, calculated_lit_value), 0).r; + } else { + // TODO: lighting calculations based on https://rr2000.cwaboard.co.uk/R4/BRENDER/TEBK_43.HTM#0 + uint calculated_lit_value = 0u; + out_palette_index = texelFetch(u_material_shade_table, ivec2(texel, calculated_lit_value), 0).r; + } } else { - // no shadetable + // no lighting out_palette_index = texel; } - - if (u_blend_enabled == 1 && out_palette_index != 0u) { - // blend_table is a 256x256 image which encodes 256 values of blending between texture and existing screen pixel for each color - // u_colour_buffer is upside down from opengl perspective. We need to sample it upside down. - uint fb_color = texelFetch(u_colour_buffer, ivec2(gl_FragCoord.x, u_viewport_height - gl_FragCoord.y), 0).r; - uint blended_color = texelFetch(u_blend_table, ivec2(out_palette_index, fb_color), 0).r; - out_palette_index = blended_color; - } } - // color 0 is always transparent + + if (u_material_blend_enabled == 1u) { + // u_colour_buffer is upside down from opengl perspective. We need to sample it upside down. + uint current_framebuffer_color = texelFetch(u_colour_buffer, ivec2(gl_FragCoord.x, u_viewport_height - gl_FragCoord.y), 0).r; + out_palette_index = texelFetch(u_material_blend_table, ivec2(out_palette_index, current_framebuffer_color), 0).r; + } + + // HACK: Pick another black color instead of palette index 0 so we can detect which pixels have been drawn this frame in FlushBuffers if (out_palette_index == 0u) { - discard; + out_palette_index = 240u; } } diff --git a/src/harness/resources/3d_vert.glsl b/src/harness/resources/3d_vert.glsl index 6bc4c81c..abf80f93 100644 --- a/src/harness/resources/3d_vert.glsl +++ b/src/harness/resources/3d_vert.glsl @@ -3,10 +3,12 @@ layout (location = 0) in vec3 a_pos; layout (location = 1) in vec3 a_normal; layout (location = 2) in vec2 a_uv; +layout (location = 3) in float a_color; out vec3 v_frag_pos; out vec3 v_normal; out vec2 v_tex_coord; +out float v_color; uniform mat4 u_model; uniform mat4 u_view; @@ -16,5 +18,6 @@ void main() { v_frag_pos = vec3(u_model * vec4(a_pos, 1.0)); v_normal = a_normal; v_tex_coord = a_uv; + v_color = a_color; gl_Position = u_projection * u_view * vec4(v_frag_pos, 1.0); }