summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOmniscient <17525998+omnisci3nce@users.noreply.github.com>2024-04-06 22:24:52 +1100
committerOmniscient <17525998+omnisci3nce@users.noreply.github.com>2024-04-06 22:24:52 +1100
commitd9f9479694d8a4d74822a876516282329db5ea3d (patch)
treebd52e7fa5fea45f1483cd47d49f980732ff404d3
parent113b038b703aeef545e86700afd6dc2095015e84 (diff)
separate pipeline for skinned meshes
-rw-r--r--assets/shaders/skinned.vert47
-rw-r--r--examples/gltf_loading/ex_gltf_loading.c1
-rw-r--r--examples/skinned_animation/ex_skinned_animation.c4
-rw-r--r--src/animation.h6
-rw-r--r--src/renderer/render.c134
-rw-r--r--src/renderer/render.h2
-rw-r--r--src/renderer/render_types.h18
-rw-r--r--src/resources/gltf.c67
8 files changed, 234 insertions, 45 deletions
diff --git a/assets/shaders/skinned.vert b/assets/shaders/skinned.vert
new file mode 100644
index 0000000..3d0c2cd
--- /dev/null
+++ b/assets/shaders/skinned.vert
@@ -0,0 +1,47 @@
+#version 410 core
+// Inputs
+layout (location = 0) in vec3 inPos;
+layout (location = 1) in vec3 inNormal;
+layout (location = 2) in vec2 inTexCoords;
+layout (location = 3) in ivec4 inBoneIndices;
+layout (location = 4) in vec4 inWeights;
+
+uniform mat4 model;
+uniform mat4 view;
+uniform mat4 projection;
+uniform mat4 lightSpaceMatrix;
+
+const int MAX_BONES = 100;
+const int MAX_BONE_INFLUENCES = 4;
+uniform mat4 finalBoneMatrices[MAX_BONES];
+
+// Output
+out VS_OUT {
+ vec3 FragPos;
+ vec3 Normal;
+ vec2 TexCoords;
+ vec4 FragPosLightSpace;
+} vs_out;
+
+void main() {
+ vec4 totalPosition = vec4(0.0f);
+ for(int i = 0 ; i < MAX_BONE_INFLUENCES ; i++) {
+ if(inBoneIndices[i] == -1)
+ continue;
+ if(inBoneIndices[i] >=MAX_BONES)
+ {
+ totalPosition = vec4(inPos,1.0f);
+ break;
+ }
+ vec4 localPosition = finalBoneMatrices[inBoneIndices[i]] * vec4(inPos,1.0f);
+ totalPosition += localPosition * inWeights[i];
+ vec3 localNormal = mat3(finalBoneMatrices[inBoneIndices[i]]) * inNormal;
+ vs_out.Normal = localNormal;
+ }
+
+ vs_out.FragPos = vec3(model * vec4(inPos, 1.0));
+ // vs_out.Normal = inNormal;
+ vs_out.TexCoords = inTexCoords;
+ vs_out.FragPosLightSpace = lightSpaceMatrix * vec4(vs_out.FragPos, 1.0);
+ gl_Position = projection * view * model * totalPosition;
+} \ No newline at end of file
diff --git a/examples/gltf_loading/ex_gltf_loading.c b/examples/gltf_loading/ex_gltf_loading.c
index 7b69127..5d53e78 100644
--- a/examples/gltf_loading/ex_gltf_loading.c
+++ b/examples/gltf_loading/ex_gltf_loading.c
@@ -56,7 +56,6 @@ int main() {
scene our_scene = { .dir_light = dir_light, .n_point_lights = 4 };
memcpy(&our_scene.point_lights, &point_lights, sizeof(point_light[4]));
-
while (!glfwWindowShouldClose(core->renderer.window)) {
currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
diff --git a/examples/skinned_animation/ex_skinned_animation.c b/examples/skinned_animation/ex_skinned_animation.c
index 9efffc6..3138256 100644
--- a/examples/skinned_animation/ex_skinned_animation.c
+++ b/examples/skinned_animation/ex_skinned_animation.c
@@ -37,7 +37,7 @@ int main() {
scene our_scene = make_default_scene();
- vec3 cam_pos = vec3_create(5, 5, 5);
+ vec3 cam_pos = vec3_create(0, 5, 8);
game_state game = {
.camera = camera_create(cam_pos, vec3_negate(cam_pos), VEC3_Y, deg_to_rad(45.0)),
.camera_euler = vec3_create(90, 0, 0),
@@ -86,7 +86,7 @@ int main() {
quat rot = quat_ident();
transform tf = transform_create(VEC3_ZERO, rot, 1.0);
- draw_model(&core->renderer, &game.camera, cube, tf, &our_scene);
+ draw_skinned_model(&core->renderer, &game.camera, cube, tf, &our_scene);
// gfx_backend_draw_frame(&core->renderer, &game.camera, model, NULL);
diff --git a/src/animation.h b/src/animation.h
index 81e150a..18d3ba9 100644
--- a/src/animation.h
+++ b/src/animation.h
@@ -28,6 +28,12 @@ typedef struct keyframes {
size_t count;
} keyframes;
+typedef struct joint {
+ char* name; // optional
+ transform transform_components;
+ mat4 local_transform;
+} joint;
+
typedef struct animation_spline {
f32* timestamps;
size_t n_timestamps;
diff --git a/src/renderer/render.c b/src/renderer/render.c
index 806979d..e420043 100644
--- a/src/renderer/render.c
+++ b/src/renderer/render.c
@@ -1,3 +1,7 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include "maths_types.h"
#include "mem.h"
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
@@ -59,6 +63,9 @@ bool renderer_init(renderer* ren) {
ren->blinn_phong =
shader_create_separate("assets/shaders/blinn_phong.vert", "assets/shaders/blinn_phong.frag");
+ ren->skinned =
+ shader_create_separate("assets/shaders/skinned.vert", "assets/shaders/blinn_phong.frag");
+
default_material_init();
return true;
@@ -158,6 +165,59 @@ void draw_mesh(renderer* ren, mesh* mesh, transform tf, material* mat, mat4* vie
draw_primitives(CEL_PRIMITIVE_TOPOLOGY_TRIANGLE, 0, num_vertices);
}
+void draw_skinned_mesh(renderer* ren, mesh* mesh, transform tf, material* mat, mat4* view,
+ mat4* proj) {
+ shader lighting_shader = ren->skinned;
+
+ // bind buffer
+ bind_mesh_vertex_buffer(ren->backend_state, mesh);
+
+ // bind textures
+ bind_texture(lighting_shader, &mat->diffuse_texture, 0); // bind to slot 0
+ bind_texture(lighting_shader, &mat->specular_texture, 1); // bind to slot 1
+ uniform_f32(lighting_shader.program_id, "material.shininess", 32.);
+
+ // upload model transform
+ mat4 trans = mat4_translation(tf.position);
+ mat4 rot = mat4_rotation(tf.rotation);
+ mat4 scale = mat4_scale(tf.scale);
+ mat4 model_tf = mat4_mult(trans, mat4_mult(rot, scale));
+
+ uniform_mat4f(lighting_shader.program_id, "model", &model_tf);
+ // upload view & projection matrices
+ uniform_mat4f(lighting_shader.program_id, "view", view);
+ uniform_mat4f(lighting_shader.program_id, "projection", proj);
+
+ // draw triangles
+ u32 num_vertices = vertex_darray_len(mesh->vertices);
+ draw_primitives(CEL_PRIMITIVE_TOPOLOGY_TRIANGLE, 0, num_vertices);
+}
+
+void draw_skinned_model(renderer* ren, camera* cam, model* model, transform tf, scene* scene) {
+ mat4 view;
+ mat4 proj;
+ camera_view_projection(cam, SCR_HEIGHT, SCR_WIDTH, &view, &proj);
+
+ set_shader(ren->skinned);
+
+ // set camera uniform
+ uniform_vec3f(ren->blinn_phong.program_id, "viewPos", &cam->position);
+ // set light uniforms
+ dir_light_upload_uniforms(ren->blinn_phong, &scene->dir_light);
+ for (int i = 0; i < scene->n_point_lights; i++) {
+ point_light_upload_uniforms(ren->blinn_phong, &scene->point_lights[i], '0' + i);
+ }
+
+ for (size_t i = 0; i < mesh_darray_len(model->meshes); i++) {
+ mesh* m = &model->meshes->data[i];
+ if (vertex_darray_len(m->vertices) == 0) {
+ continue;
+ }
+ material* mat = &model->materials->data[m->material_index];
+ draw_skinned_mesh(ren, m, tf, mat, &view, &proj);
+ }
+}
+
void model_upload_meshes(renderer* ren, model* model) {
INFO("Upload mesh vertex data to GPU for model %s", model->name);
@@ -173,6 +233,7 @@ void model_upload_meshes(renderer* ren, model* model) {
// upload each mesh to the GPU
for (int mesh_i = 0; mesh_i < num_meshes; mesh_i++) {
+ mesh mesh = model->meshes->data[mesh_i];
model->meshes->data[mesh_i].vao = VAOs[mesh_i];
model->meshes->data[mesh_i].vbo = VBOs[mesh_i];
// 3. bind buffers
@@ -185,34 +246,51 @@ void model_upload_meshes(renderer* ren, model* model) {
// TODO: convert this garbage into a function
f32 verts[num_vertices * 8];
// for each face
- for (int i = 0; i < (num_vertices / 3); i++) {
- // for each vert in face
- for (int j = 0; j < 3; j++) {
- size_t stride = (i * 24) + j * 8;
- // printf("i: %d, stride: %ld, loc %d\n", i, stride, i * 3 + j);
- vertex vert = model->meshes->data[mesh_i].vertices->data[i];
- // printf("pos %f %f %f\n", vert.position.x, vert.position.y, vert.position.z);
- // printf("norm %f %f %f\n", vert.normal.x, vert.normal.y, vert.normal.z);
- // printf("tex %f %f\n", vert.uv.x, vert.uv.y);
- verts[stride + 0] =
- ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].position.x;
- verts[stride + 1] =
- ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].position.y;
- verts[stride + 2] =
- ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].position.z;
- verts[stride + 3] =
- ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].normal.x;
- verts[stride + 4] =
- ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].normal.y;
- verts[stride + 5] =
- ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].normal.z;
- verts[stride + 6] = ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].uv.x;
- verts[stride + 7] = ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].uv.y;
- }
+ // for (int i = 0; i < (num_vertices / 3); i++) {
+ // // for each vert in face
+ // for (int j = 0; j < 3; j++) {
+ // size_t stride = (i * 24) + j * 8;
+ // // printf("i: %d, stride: %ld, loc %d\n", i, stride, i * 3 + j);
+ // vertex vert = model->meshes->data[mesh_i].vertices->data[i];
+ // // printf("pos %f %f %f\n", vert.position.x, vert.position.y, vert.position.z);
+ // // printf("norm %f %f %f\n", vert.normal.x, vert.normal.y, vert.normal.z);
+ // // printf("tex %f %f\n", vert.uv.x, vert.uv.y);
+ // verts[stride + 0] =
+ // ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].position.x;
+ // verts[stride + 1] =
+ // ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].position.y;
+ // verts[stride + 2] =
+ // ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].position.z;
+ // verts[stride + 3] =
+ // ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].normal.x;
+ // verts[stride + 4] =
+ // ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].normal.y;
+ // verts[stride + 5] =
+ // ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].normal.z;
+ // verts[stride + 6] = ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 +
+ // j].uv.x; verts[stride + 7] = ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3
+ // + j].uv.y;
+ // }
+ // }
+ size_t vertex_size = mesh.is_skinned
+ ? sizeof(vec3) * 2 + sizeof(vec2) + sizeof(u32) * 4 + sizeof(vec4)
+ : sizeof(vec3) * 2 + sizeof(vec2);
+ if (!mesh.is_skinned) {
+ printf("sizeof(vertex) -> %ld, vertex_size -> %ld\n", sizeof(vertex), vertex_size);
+ assert(vertex_size == sizeof(vertex));
+ }
+ size_t buffer_size = vertex_size * num_vertices;
+ u8* bytes = malloc(buffer_size);
+
+ for (int i = 0; i < num_vertices; i++) {
+ u8* p = bytes + vertex_size * i;
+ u8* bone_data_offset = p + sizeof(u32) * 4 + sizeof(vec4);
+ memcpy(p, &mesh.vertices->data[i], sizeof(vertex));
+ memcpy(bone_data_offset, &mesh.vertex_bone_data->data[i], sizeof(vertex_bone_data));
}
// 4. upload data
- glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
+ glBufferData(GL_ARRAY_BUFFER, buffer_size, bytes, GL_STATIC_DRAW);
// 5. cont. set mesh vertex layout
glBindVertexArray(model->meshes->data[mesh_i].vao);
@@ -225,6 +303,10 @@ void model_upload_meshes(renderer* ren, model* model) {
// tex coords
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2);
+
+ // skinning (optional)
+ if (mesh.is_skinned) {
+ }
}
INFO("Uploaded %d submeshes with a total of %d vertices\n", num_meshes, total_verts);
@@ -242,7 +324,7 @@ texture texture_data_load(const char* path, bool invert_y) {
stbi_set_flip_vertically_on_load(invert_y);
#pragma GCC diagnostic ignored "-Wpointer-sign"
- char* data = stbi_load(path, &width, &height, &num_channels, 0); // STBI_rgb_alpha);
+ char* data = stbi_load(path, &width, &height, &num_channels, 0); // STBI_rgb_alpha);
if (data) {
DEBUG("loaded texture: %s", path);
} else {
diff --git a/src/renderer/render.h b/src/renderer/render.h
index f0118b6..ba2cbdc 100644
--- a/src/renderer/render.h
+++ b/src/renderer/render.h
@@ -35,6 +35,8 @@ void model_upload_meshes(renderer* ren, model* model);
void draw_model(renderer* ren, camera* camera, model* model, transform tf, scene* scene);
void draw_mesh(renderer* ren, mesh* mesh, transform tf, material* mat, mat4* view, mat4* proj);
+void draw_skinned_model(renderer* ren, camera* cam, model* model, transform tf, scene* scene);
+
void model_destroy(model* model);
// ---
diff --git a/src/renderer/render_types.h b/src/renderer/render_types.h
index e4d3127..61571e1 100644
--- a/src/renderer/render_types.h
+++ b/src/renderer/render_types.h
@@ -46,6 +46,7 @@ typedef struct renderer {
renderer_config config;
// shaders
shader blinn_phong;
+ shader skinned;
} renderer;
// --- Lighting & Materials
@@ -118,24 +119,25 @@ typedef struct vertex {
vec2 uv;
} vertex;
-typedef struct skinned_vertex {
- vec3 position;
- vec3 normal;
- vec2 uv;
+typedef struct vertex_bone_data {
vec4i joints; /** @brief 4 indices of joints that influence vectors position */
vec4 weights; /** @brief weight (0,1) of each joint */
-} skinned_vertex;
+} vertex_bone_data;
+#include "animation.h"
#ifndef TYPED_VERTEX_ARRAY
KITC_DECL_TYPED_ARRAY(vertex) // creates "vertex_darray"
-KITC_DECL_TYPED_ARRAY(skinned_vertex) // creates "skinned_vertex_darray"
+KITC_DECL_TYPED_ARRAY(vertex_bone_data) // creates "skinned_vertex_darray"
+KITC_DECL_TYPED_ARRAY(joint)
#define TYPED_VERTEX_ARRAY
#endif
+
typedef struct mesh {
vertex_darray* vertices;
- // skinned_vertex_darray* skinned_vertices; // only used if model needs it
- // bool is_skinned;
+ vertex_bone_data_darray* vertex_bone_data; // only used if model needs it
+ joint_darray* bones;
+ bool is_skinned;
u32 vertex_size; /** size in bytes of each vertex including necessary padding */
bool has_indices;
u32 *indices;
diff --git a/src/resources/gltf.c b/src/resources/gltf.c
index 7fbef3b..c7c1f55 100644
--- a/src/resources/gltf.c
+++ b/src/resources/gltf.c
@@ -7,6 +7,7 @@
#include "file.h"
#include "loaders.h"
#include "log.h"
+#include "maths.h"
#include "maths_types.h"
#include "mem.h"
#include "path.h"
@@ -30,6 +31,7 @@ KITC_DECL_TYPED_ARRAY(u32)
KITC_DECL_TYPED_ARRAY(vec4i)
KITC_DECL_TYPED_ARRAY(vec4)
KITC_DECL_TYPED_ARRAY(face)
+// KITC_DECL_TYPED_ARRAY(joint)
bool model_load_gltf_str(const char *file_string, const char *filepath, str8 relative_path,
model *out_model, bool invert_textures_y);
@@ -84,9 +86,9 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel
vec3_darray *tmp_positions = vec3_darray_new(1000);
vec3_darray *tmp_normals = vec3_darray_new(1000);
vec2_darray *tmp_uvs = vec2_darray_new(1000);
- face_darray *tmp_faces = face_darray_new(1000);
- vec4i_darray *tmp_joints = vec4i_darray_new(1000);
+ vec4i_darray *tmp_joint_indices = vec4i_darray_new(1000);
vec4_darray *tmp_weights = vec4_darray_new(1000);
+ joint_darray *tmp_joints = joint_darray_new(256);
cgltf_options options = { 0 };
cgltf_data *data = NULL;
@@ -112,7 +114,33 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel
if (is_skinned) {
cgltf_skin *gltf_skin = data->skins;
- TRACE("loading skin %s", gltf_skin->name);
+ DEBUG("loading skin %s", gltf_skin->name);
+ size_t num_joints = gltf_skin->joints_count;
+ DEBUG("# Joints %d", num_joints);
+
+ // for each one we'll spit out a joint
+ for (size_t i = 0; i < num_joints; i++) {
+ cgltf_node *joint_node = gltf_skin->joints[i];
+
+ joint joint_i = { .name = "testjoint" };
+ if (joint_node->children_count > 0 && !joint_node->has_translation &&
+ !joint_node->has_rotation) {
+ WARN("joint Node with index %d is the root node", i);
+ joint_i.transform_components = TRANSFORM_DEFAULT;
+ } else {
+ TRACE("Storing joint transform");
+ joint_i.transform_components = TRANSFORM_DEFAULT;
+ if (joint_node->has_translation) {
+ memcpy(&joint_i.transform_components.position, &joint_node->translation, 3 * sizeof(f32));
+ }
+ if (joint_node->has_rotation) {
+ memcpy(&joint_i.transform_components.rotation, &joint_node->rotation, 4 * sizeof(f32));
+ }
+ // TODO: support scaling as vec instead of float
+ }
+ joint_i.local_transform = transform_to_mat(&joint_i.transform_components);
+ joint_darray_push(tmp_joints, joint_i);
+ }
}
// --- Materials
@@ -218,9 +246,9 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel
joint_indices.y = (u32)joints_as_floats.y;
joint_indices.z = (u32)joints_as_floats.z;
joint_indices.w = (u32)joints_as_floats.w;
- printf("Joints affecting %d %d %d %d\n", joint_indices.x, joint_indices.y,
- joint_indices.z, joint_indices.w);
- vec4i_darray_push(tmp_joints, joint_indices);
+ // printf("Joints affecting %d %d %d %d\n", joint_indices.x, joint_indices.y,
+ // joint_indices.z, joint_indices.w);
+ vec4i_darray_push(tmp_joint_indices, joint_indices);
}
} else if (attribute.type == cgltf_attribute_type_weights) {
@@ -232,7 +260,7 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel
for (cgltf_size v = 0; v < accessor->count; ++v) {
vec4 weights;
cgltf_accessor_read_float(accessor, v, &weights.x, 4);
- printf("Weights affecting %f %f %f %f\n", weights.x, weights.y, weights.z, weights.w);
+ // printf("Weights affecting %f %f %f %f\n", weights.x, weights.y, weights.z, weights.w);
vec4_darray_push(tmp_weights, weights);
}
} else {
@@ -240,7 +268,7 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel
}
}
- mesh mesh = {0};
+ mesh mesh = { 0 };
mesh.vertices = vertex_darray_new(10);
cgltf_accessor *indices = primitive.indices;
@@ -282,7 +310,30 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel
}
}
+ if (is_skinned) {
+ mesh.vertex_bone_data = vertex_bone_data_darray_new(tmp_joint_indices->len);
+ mesh.bones = joint_darray_new(tmp_joints->len);
+ for (int i = 0; i < tmp_joint_indices->len; i++) {
+ vertex_bone_data data;
+ data.joints = tmp_joint_indices->data[i];
+ data.weights =tmp_weights->data[i];
+ vertex_bone_data_darray_push(mesh.vertex_bone_data, data);
+ }
+ for (int i = 0; i < tmp_joints->len; i++) {
+ joint data = tmp_joints->data[i];
+ joint_darray_push(mesh.bones, data);
+ }
+ }
+
mesh_darray_push(out_model->meshes, mesh);
+
+ // clear data for each mesh
+ vec3_darray_clear(tmp_positions);
+ vec3_darray_clear(tmp_normals);
+ vec2_darray_free(tmp_uvs);
+ vec4i_darray_clear(tmp_joint_indices);
+ vec4_darray_clear(tmp_weights);
+ joint_darray_clear(tmp_joints);
}
for (int i = 0; i < out_model->meshes->len; i++) {