From 053ff80c390edbf99b56bd303558a5f0a503f5e3 Mon Sep 17 00:00:00 2001 From: Cameron Reikes Date: Sun, 2 Jul 2023 20:36:30 -0700 Subject: [PATCH] Upload bone transforms as texture, drastically increases max bone count from 4 --- armature.glsl | 29 ++++++++++++++++++++-- main.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 92 insertions(+), 6 deletions(-) diff --git a/armature.glsl b/armature.glsl index 5e494e7..884436e 100644 --- a/armature.glsl +++ b/armature.glsl @@ -13,8 +13,20 @@ uniform vs_params { mat4 model; mat4 view; mat4 projection; - mat4 bones[4]; + vec2 bones_tex_size; }; +uniform sampler2D bones_tex; + +// in textures, color elements are delivered as unsigend normalize floats +// in [0, 1]. This makes them into [-1, 1] as the bone matrices require +// such values to be correct +vec4 make_signed_again(vec4 v) { + v.x = 2.0 * v.x - 1.0; + v.y = 2.0 * v.y - 1.0; + v.z = 2.0 * v.z - 1.0; + v.w = 2.0 * v.w - 1.0; + return v; +} void main() { vec4 total_position = vec4(0.0f); @@ -25,7 +37,20 @@ void main() { int index = int(index_float * 65535.0); float weight = weights_in[i]; - vec4 local_position = bones[index] * vec4(pos_in, 1.0f); + float y_coord = (0.5 + index)/bones_tex_size.y; + vec4 col0 = texture(bones_tex, vec2((0.5 + 0)/bones_tex_size.x, y_coord)); + vec4 col1 = texture(bones_tex, vec2((0.5 + 1)/bones_tex_size.x, y_coord)); + vec4 col2 = texture(bones_tex, vec2((0.5 + 2)/bones_tex_size.x, y_coord)); + vec4 col3 = texture(bones_tex, vec2((0.5 + 3)/bones_tex_size.x, y_coord)); + + col0 = make_signed_again(col0); + col1 = make_signed_again(col1); + col2 = make_signed_again(col2); + col3 = make_signed_again(col3); + + mat4 bone_mat = mat4(col0, col1, col2, col3); + + vec4 local_position = bone_mat * vec4(pos_in, 1.0f); total_position += local_position * weight; } diff --git a/main.c b/main.c index e7275fa..fd8b1ce 100644 --- a/main.c +++ b/main.c @@ -972,7 +972,10 @@ typedef struct ArmatureVertex *vertices; MD_u64 vertices_length; sg_buffer loaded_buffer; + sg_image bones_texture; MD_String8 name; + int bones_texture_width; + int bones_texture_height; } Armature; Armature load_armature(MD_Arena *arena, MD_String8 binary_file, MD_String8 armature_name) @@ -1080,6 +1083,19 @@ Armature load_armature(MD_Arena *arena, MD_String8 binary_file, MD_String8 armat .label = (const char*)nullterm(arena, MD_S8Fmt(arena, "%.*s-vertices", MD_S8VArg(armature_name))).str, }); + to_return.bones_texture_width = 4; + to_return.bones_texture_height = (int)to_return.bones_length; + + Log("Amrature %.*s has bones texture size (%d, %d)\n", MD_S8VArg(armature_name), to_return.bones_texture_width, to_return.bones_texture_height); + to_return.bones_texture = sg_make_image(&(sg_image_desc) { + .width = to_return.bones_texture_width, + .height = to_return.bones_texture_height, + .pixel_format = SG_PIXELFORMAT_RGBA8, + .min_filter = SG_FILTER_NEAREST, + .mag_filter = SG_FILTER_NEAREST, + .usage = SG_USAGE_STREAM, + }); + // a sanity check SLICE_ITER(Bone, to_return.bones) { @@ -2643,9 +2659,15 @@ int num_vertices = 0; void draw_placed(Mat4 view, Mat4 projection, PlacedMesh *cur) { + sg_apply_pipeline(state.threedee_pip); + Mesh *drawing = cur->draw_with; + state.threedee_bind.fs_images[SLOT_threedee_tex] = image_gigatexture; + ARR_ITER(sg_image, state.threedee_bind.vs_images) + { + *it = (sg_image){0}; + } state.threedee_bind.vertex_buffers[0] = drawing->loaded_buffer; - sg_apply_pipeline(state.threedee_pip); sg_apply_bindings(&state.threedee_bind); Mat4 model = transform_to_mat(cur->t); @@ -2685,9 +2707,8 @@ Mat4 get_animated_bone_transform(Bone *bone, float time) void draw_armature(Mat4 view, Mat4 projection, Transform t, Armature *armature, float elapsed_time) { - state.threedee_bind.vertex_buffers[0] = armature->loaded_buffer; + MD_ArenaTemp scratch = MD_GetScratch(0, 0); sg_apply_pipeline(state.armature_pip); - sg_apply_bindings(&state.threedee_bind); Mat4 model = transform_to_mat(t); @@ -2695,6 +2716,11 @@ void draw_armature(Mat4 view, Mat4 projection, Transform t, Armature *armature, memcpy(params.model, (float*)&model, sizeof(model)); memcpy(params.view, (float*)&view, sizeof(view)); memcpy(params.projection, (float*)&projection, sizeof(projection)); + params.bones_tex_size[0] = (float)armature->bones_texture_width; + params.bones_tex_size[1] = (float)armature->bones_texture_height; + + int bones_tex_size = 4 * armature->bones_texture_width * armature->bones_texture_height; + MD_u8 *bones_tex = MD_ArenaPush(scratch.arena, bones_tex_size); for(MD_u64 i = 0; i < armature->bones_length; i++) { @@ -2708,13 +2734,48 @@ void draw_armature(Mat4 view, Mat4 projection, Transform t, Armature *armature, final = MulM4(get_animated_bone_transform(cur_in_hierarchy, elapsed_time), final); } - memcpy(params.bones[i], (float*)&final, sizeof(final)); + for(int col = 0; col < 4; col++) + { + Vec4 to_upload = final.Columns[col]; + assert(-1.1f <= to_upload.x && to_upload.x <= 1.1f); + assert(-1.1f <= to_upload.y && to_upload.y <= 1.1f); + assert(-1.1f <= to_upload.z && to_upload.z <= 1.1f); + assert(-1.1f <= to_upload.w && to_upload.w <= 1.1f); + + // make them normalized + to_upload.x = to_upload.x/2.0f + 0.5f; + to_upload.y = to_upload.y/2.0f + 0.5f; + to_upload.z = to_upload.z/2.0f + 0.5f; + to_upload.w = to_upload.w/2.0f + 0.5f; + + to_upload.x = clamp01(to_upload.x); + to_upload.y = clamp01(to_upload.y); + to_upload.z = clamp01(to_upload.z); + to_upload.w = clamp01(to_upload.w); + + int bytes_per_pixel = 4; + int bytes_per_row = bytes_per_pixel * 4; + bones_tex[bytes_per_pixel*col + bytes_per_row*i + 0] = (MD_u8)(to_upload.x * 255.0); + bones_tex[bytes_per_pixel*col + bytes_per_row*i + 1] = (MD_u8)(to_upload.y * 255.0); + bones_tex[bytes_per_pixel*col + bytes_per_row*i + 2] = (MD_u8)(to_upload.z * 255.0); + bones_tex[bytes_per_pixel*col + bytes_per_row*i + 3] = (MD_u8)(to_upload.w * 255.0); + } } + sg_update_image(armature->bones_texture, &(sg_image_data){ + .subimage[0][0] = (sg_range){bones_tex, bones_tex_size}, + }); + + state.threedee_bind.vertex_buffers[0] = armature->loaded_buffer; + state.threedee_bind.vs_images[SLOT_armature_bones_tex] = armature->bones_texture; + state.threedee_bind.fs_images[SLOT_armature_tex] = image_gigatexture; + sg_apply_bindings(&state.threedee_bind); sg_apply_uniforms(SG_SHADERSTAGE_VS, SLOT_armature_vs_params, &SG_RANGE(params)); num_draw_calls += 1; num_vertices += (int)armature->vertices_length; sg_draw(0, (int)armature->vertices_length, 1); + + MD_ReleaseScratch(scratch); }