You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

334 lines
12 KiB
C

// 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);
}
}
}
}
}
}