// The Digital Grove Codebase // Copyright (c) Ryan Fleury. All rights reserved. //////////////////////////////// //~ rjf: Render Resources @per_platform function R_BufferHandle R_BufferHandleFromData(void *data, UAddr element_count, UAddr element_size) { R_BufferHandle result = {0}; if(n64_r_state.buffers_count < ArrayCount(n64_r_state.buffers)) { N64_R_Buffer *buffer = &n64_r_state.buffers[n64_r_state.buffers_count]; buffer->data = data; buffer->element_count = element_count; buffer->element_size = element_size; n64_r_state.buffers_count += 1; result.uaddr[0] = n64_r_state.buffers_count; } return result; } function R_SubModelHandle R_SubModelHandleFromBuffers(R_BufferHandle vtx_buffer, R_BufferHandle idx_buffer) { R_SubModelHandle result = {0}; if(n64_r_state.sub_models_count < ArrayCount(n64_r_state.sub_models)) { N64_R_SubModel *sub_model= &n64_r_state.sub_models[n64_r_state.sub_models_count]; sub_model->vtx_buffer_num = (U16)vtx_buffer.uaddr[0]; sub_model->idx_buffer_num = (U16)idx_buffer.uaddr[0]; n64_r_state.sub_models_count += 1; result.uaddr[0] = n64_r_state.sub_models_count; } return result; } //////////////////////////////// //~ rjf: Render Commands @per_platform function void R_Pass(R_PassParams *params) { MemoryCopyStruct(&n64_r_state.pass_params, params); n64_r_state.pass_view_projection = Mul4x4F32(params->projection, params->view); n64_r_state.buffers_count = 0; n64_r_state.sub_models_count = 0; //- rjf: draw sky { // rjf: sync pipe { N64_DP_Cmd_Stub cmd = {0}; cmd.id = N64_DP_CmdID_SyncPipe; WriteStructAndInc(n64_r_state.rdp_command_buffer, &cmd, &n64_r_state.rdp_command_buffer_write_pos); } // rjf: set other modes { N64_DP_Cmd_SetOtherModes cmd = {0}; cmd.id = N64_DP_CmdID_SetOtherModes; cmd.atomic_prim = 1; WriteStructAndInc(n64_r_state.rdp_command_buffer, &cmd, &n64_r_state.rdp_command_buffer_write_pos); } // rjf: draw full-screen triangle for sky { N64_DP_Cmd_FillTriangleBase cmd = {0}; cmd.id = N64_DP_CmdID_FillTriangleS; cmd.flags = N64_DP_Cmd_FillTriangleFlag_LeftMajor; cmd.yl = Fixed112FromF32(N64_FB_HEIGHT); cmd.ym = Fixed112FromF32(N64_FB_HEIGHT); cmd.yh = 0; cmd.xl = Fixed1616FromF32(N64_FB_WIDTH); cmd.dxldy = 0; cmd.xh = 0; cmd.dxhdy = 0; cmd.xm = Fixed1616FromF32(N64_FB_WIDTH); cmd.dxmdy = 0; WriteStructAndInc(n64_r_state.rdp_command_buffer, &cmd, &n64_r_state.rdp_command_buffer_write_pos); { F32 pitch = 0.1f; Vec4F32 top_color_4f32 = V4F32(0.02f, 0.2f - 0.1f*(0.75f + pitch/0.25f), 0.05f - 0.02f*(0.75f + pitch/0.25f), 1.f); // top_color_4f32 = V4F32(0.02f, 0.15f - 0.1f*(0.75f + pitch/0.25f), 0.3f - 0.02f*(0.75f + pitch/0.25f), 1.f); S32 top_color_x_s32 = Fixed1616FromF32(top_color_4f32.x*255.f); S32 top_color_y_s32 = Fixed1616FromF32(top_color_4f32.y*255.f); S32 top_color_z_s32 = Fixed1616FromF32(top_color_4f32.z*255.f); S32 top_color_w_s32 = Fixed1616FromF32(top_color_4f32.w*255.f); S32 dcdy = Fixed1616FromF32(-0.1f); S32 dcpdy = Fixed1616FromF32(+0.01f); N64_DP_CmdExt_FillTriangleShading ext = {0}; ext.r_i = Upper16(top_color_x_s32); ext.g_i = Upper16(top_color_y_s32); ext.b_i = Upper16(top_color_z_s32); ext.a_i = Upper16(top_color_w_s32); ext.r_f = Lower16(top_color_x_s32); ext.g_f = Lower16(top_color_y_s32); ext.b_f = Lower16(top_color_z_s32); ext.a_f = Lower16(top_color_w_s32); ext.drde_i = Upper16(dcdy); ext.dgde_i = Upper16(dcdy); ext.dbde_i = Upper16(dcpdy); ext.drde_f = Lower16(dcdy); ext.dgde_f = Lower16(dcdy); ext.dbde_f = Lower16(dcpdy); WriteStructAndInc(n64_r_state.rdp_command_buffer, &ext, &n64_r_state.rdp_command_buffer_write_pos); } } } //- rjf: set up depth tested drawing parameters { // rjf: sync pipe { N64_DP_Cmd_Stub cmd = {0}; cmd.id = N64_DP_CmdID_SyncPipe; WriteStructAndInc(n64_r_state.rdp_command_buffer, &cmd, &n64_r_state.rdp_command_buffer_write_pos); } // rjf: set other modes { N64_DP_Cmd_SetOtherModes cmd = {0}; cmd.id = N64_DP_CmdID_SetOtherModes; cmd.atomic_prim = 1; cmd.z_update_en = 1; cmd.z_compare_en = 1; cmd.antialias_en = 1; WriteStructAndInc(n64_r_state.rdp_command_buffer, &cmd, &n64_r_state.rdp_command_buffer_write_pos); } } } function void R_SubModel(R_SubModelHandle handle, R_Material material, Mat4x4F32 xform) { Mat4x4F32 view_projection = n64_r_state.pass_view_projection; UAddr sub_model_num = handle.uaddr[0]; if(0 < sub_model_num && sub_model_num <= n64_r_state.sub_models_count) { N64_R_SubModel *sub_model = &n64_r_state.sub_models[sub_model_num-1]; if(0 < sub_model->vtx_buffer_num && sub_model->vtx_buffer_num <= n64_r_state.buffers_count && 0 < sub_model->idx_buffer_num && sub_model->idx_buffer_num <= n64_r_state.buffers_count) { N64_R_Buffer *vtx_buffer = &n64_r_state.buffers[sub_model->vtx_buffer_num-1]; N64_R_Buffer *idx_buffer = &n64_r_state.buffers[sub_model->idx_buffer_num-1]; Vec3F32 material_rgb = material.color.xyz; if(vtx_buffer->element_size == sizeof(Vec3F32) && (idx_buffer->element_size == 2 || idx_buffer->element_size == 4)) { UAddr tri_count = idx_buffer->element_count/3; Vec3F32 *vtx = (Vec3F32 *)vtx_buffer->data; UAddr vtx_count = vtx_buffer->element_count; UAddr command_bytes_per_tri = sizeof(N64_DP_Cmd_FillTriangleBase) + sizeof(N64_DP_CmdExt_FillTriangleShading) + sizeof(N64_DP_CmdExt_FillTriangleDepth); UAddr command_bytes_left = ArrayCount(n64_r_state.rdp_command_buffer) - n64_r_state.rdp_command_buffer_write_pos; UAddr tri_count_max = command_bytes_left / command_bytes_per_tri; UAddr tri_count_actual = Min(tri_count_max, tri_count); for EachIndex(tri_idx, tri_count_actual) { // rjf: get face nums for this triangle UAddr face_nums[3]; switch(idx_buffer->element_size) { default:{MemoryZero(face_nums, sizeof(face_nums));}break; case 2: { face_nums[0] = ((U16 *)idx_buffer->data)[tri_idx*3+0]; face_nums[1] = ((U16 *)idx_buffer->data)[tri_idx*3+1]; face_nums[2] = ((U16 *)idx_buffer->data)[tri_idx*3+2]; }break; case 4: { face_nums[0] = ((U32 *)idx_buffer->data)[tri_idx*3+0]; face_nums[1] = ((U32 *)idx_buffer->data)[tri_idx*3+1]; face_nums[2] = ((U32 *)idx_buffer->data)[tri_idx*3+2]; }break; } // rjf: unpack world-space vertices Vec4F32 world_v[3] = { V4F32(vtx[face_nums[0]-1].x, vtx[face_nums[0]-1].y, vtx[face_nums[0]-1].z, 1.f), V4F32(vtx[face_nums[1]-1].x, vtx[face_nums[1]-1].y, vtx[face_nums[1]-1].z, 1.f), V4F32(vtx[face_nums[2]-1].x, vtx[face_nums[2]-1].y, vtx[face_nums[2]-1].z, 1.f), }; // rjf: world -> clip Vec4F32 clip_v[] = { XForm4F32(view_projection, world_v[0]), XForm4F32(view_projection, world_v[1]), XForm4F32(view_projection, world_v[2]), }; // rjf: clip -> ndc Vec3F32 ndc_v[] = { Scale3F32(clip_v[0].xyz, 1.f/clip_v[0].w), Scale3F32(clip_v[1].xyz, 1.f/clip_v[1].w), Scale3F32(clip_v[2].xyz, 1.f/clip_v[2].w), }; // rjf: early discard of back-facing triangles { Vec3F32 normal = Cross3F32(Sub3F32(ndc_v[1], ndc_v[0]), Sub3F32(ndc_v[2], ndc_v[0])); Vec3F32 eye = V3F32(0, 0, 1); F32 dot = Dot3F32(normal, eye); if(dot > 0) { continue; } } // rjf: ndc -> screen Vec3F32 ndc2screen_v = V3F32(N64_FB_WIDTH/2.f, N64_FB_HEIGHT/2.f, 0x7FFF/2.f); Vec3F32 screen_v[] = { Mul3F32(Add3F32(Mul3F32(ndc_v[0], V3F32(-1, -1, 1)), V3F32(1, 1, 1)), ndc2screen_v), Mul3F32(Add3F32(Mul3F32(ndc_v[1], V3F32(-1, -1, 1)), V3F32(1, 1, 1)), ndc2screen_v), Mul3F32(Add3F32(Mul3F32(ndc_v[2], V3F32(-1, -1, 1)), V3F32(1, 1, 1)), ndc2screen_v), }; // rjf: screen -> snapped; ensures joining edges of triangles, otherwise triangles // disagree on edges in projected space, causing seams. for EachElement(idx, screen_v) { for EachIndex(axis, 3) { screen_v[idx].v[axis] = FloorF32(screen_v[idx].v[axis]*4.f)/4.f; } } // rjf: sort vertices into top/mid/bot Vec3F32 top = screen_v[0]; Vec3F32 mid = screen_v[1]; Vec3F32 bot = screen_v[2]; U8 world_idxs[] = { 0, 1, 2 }; if(mid.y > bot.y) { Swap(Vec3F32, mid, bot); } if(top.y > mid.y) { Swap(Vec3F32, top, mid); } if(mid.y > bot.y) { Swap(Vec3F32, mid, bot); } // rjf: unpack triangle edges // // * // |\ "M":top2mid // | \ // "H":top2bot | * // | / // |/ "L":mid2bot // * // Vec3F32 top2mid = Sub3F32(mid, top); Vec3F32 top2bot = Sub3F32(bot, top); Vec3F32 mid2bot = Sub3F32(bot, mid); F32 top2bot_invslope = (top2bot.y != 0) ? (top2bot.x / top2bot.y) : 0; F32 top2mid_invslope = (top2mid.y != 0) ? (top2mid.x / top2mid.y) : 0; F32 mid2bot_invslope = (mid2bot.y != 0) ? (mid2bot.x / mid2bot.y) : 0; F32 fy = FloorF32(top.y) - top.y; F32 nz = top2bot.x*top2mid.y - top2bot.y*top2mid.x; F32 inv_n = (nz != 0.f) ? (-1.f / nz) : 0.f; // rjf: fill command N64_DP_Cmd_FillTriangleBase cmd = {0}; F32 x_h_at_mid = top.x + (mid.y - top.y) * top2bot_invslope; B32 left_major = (mid.x >= x_h_at_mid); cmd.id = N64_DP_CmdID_FillTriangleSZ; cmd.flags = left_major ? N64_DP_Cmd_FillTriangleFlag_LeftMajor : 0; cmd.yl = Fixed112FromF32(bot.y); cmd.ym = Fixed112FromF32(mid.y); cmd.yh = Fixed112FromF32(top.y); cmd.xl = Fixed1616FromF32(mid.x); cmd.dxldy = Fixed1616FromF32(mid2bot_invslope); cmd.xh = Fixed1616FromF32(top.x + fy*top2bot_invslope); cmd.dxhdy = Fixed1616FromF32(top2bot_invslope); cmd.xm = Fixed1616FromF32(top.x + fy*top2mid_invslope); cmd.dxmdy = Fixed1616FromF32(top2mid_invslope); // rjf: push WriteStructAndInc(n64_r_state.rdp_command_buffer, &cmd, &n64_r_state.rdp_command_buffer_write_pos); // rjf: unpack lighting Vec3F32 normal = Cross3F32(Sub3F32(world_v[2].xyz, world_v[0].xyz), Sub3F32(world_v[1].xyz, world_v[0].xyz)); Vec3F32 keylight_dir = n64_r_state.pass_params.keylight_dir; F32 normal_dot_keylight_dir = Dot3F32(Normalize3F32(keylight_dir), Normalize3F32(normal)); F32 light_factor_unclamped = 1 - Clamp(0, -normal_dot_keylight_dir, 1); F32 light_factor = Clamp(0.2f, light_factor_unclamped, 1.f); // rjf: unpack vertex colors Vec3F32 rgb_v[] = { Scale3F32(material_rgb, light_factor), Scale3F32(material_rgb, light_factor), Scale3F32(material_rgb, light_factor), }; // rjf: extend w/ shading { N64_DP_CmdExt_FillTriangleShading ext = {0}; ext.r_i = 255*rgb_v[0].x; ext.g_i = 255*rgb_v[0].y; ext.b_i = 255*rgb_v[0].z; ext.a_i = 255; WriteStructAndInc(n64_r_state.rdp_command_buffer, &ext, &n64_r_state.rdp_command_buffer_write_pos); } // rjf: extend w/ depth { F32 dzdx = (top2bot.y*top2mid.z - top2mid.y*top2bot.z) * inv_n; F32 dzdy = (top2mid.x*top2bot.z - top2bot.x*top2mid.z) * inv_n; F32 dzde = dzdy + dzdx*top2bot_invslope; N64_DP_CmdExt_FillTriangleDepth ext = {0}; ext.z = Fixed1616FromF32(top.z + fy * dzde); ext.dzdx = Fixed1616FromF32(dzdx); ext.dzdy = Fixed1616FromF32(dzdy); ext.dzde = Fixed1616FromF32(dzde); WriteStructAndInc(n64_r_state.rdp_command_buffer, &ext, &n64_r_state.rdp_command_buffer_write_pos); } } } } } }