Import .glb model file with animation
- improve movement controls - TODO: allow more than one model, instance, and animation
This commit is contained in:
168
src/render.c
168
src/render.c
@@ -7,6 +7,7 @@
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb/stb_image.h"
|
||||
#include "cglm/cglm.h"
|
||||
#include "cgltf/cgltf.h"
|
||||
#include "render.h"
|
||||
#include "camera.h"
|
||||
#include "game.h"
|
||||
@@ -125,15 +126,13 @@ static int CompileShader(GLchar *sourcePath, GLenum shaderType, char ***outCode,
|
||||
return shaderId;
|
||||
}
|
||||
|
||||
static int LoadShaders()
|
||||
static int LoadShaders(const char *vertShaderFilePath, const char *fragShaderFilePath)
|
||||
{
|
||||
const char *vertShaderFilePath = "./res/glsl/vertex.glsl";
|
||||
char **vertCode = NULL;
|
||||
int vertNumLines;
|
||||
int vertShaderId = CompileShader(vertShaderFilePath, GL_VERTEX_SHADER, &vertCode, &vertNumLines);
|
||||
if (vertShaderId < 0) return vertShaderId;
|
||||
|
||||
const char *fragShaderFilePath = "./res/glsl/fragment.glsl";
|
||||
char **fragCode = NULL;
|
||||
int fragNumLines;
|
||||
int fragShaderId = CompileShader(fragShaderFilePath, GL_FRAGMENT_SHADER, &fragCode, &fragNumLines);
|
||||
@@ -223,9 +222,10 @@ bool Render_Init(GameState *gs)
|
||||
printf("Created OpenGL window.\n");
|
||||
|
||||
glewInit();
|
||||
gs->shaderProgramId = LoadShaders();
|
||||
gs->shaderProgramId = LoadShaders("./res/glsl/vertex.glsl", "./res/glsl/fragment.glsl");
|
||||
gs->modelShaderProgramId = LoadShaders("./res/glsl/model_vertex.glsl", "./res/glsl/fragment.glsl");
|
||||
|
||||
if (gs->shaderProgramId < 0)
|
||||
if (gs->shaderProgramId < 0 || gs->modelShaderProgramId < 0)
|
||||
{
|
||||
printf("Failed to load shaders.\n");
|
||||
return false;
|
||||
@@ -238,7 +238,7 @@ bool Render_Init(GameState *gs)
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
printf("Initialized OpenGL.\n");
|
||||
|
||||
gs->numShapes = 3;
|
||||
gs->numShapes = 2;
|
||||
|
||||
Shape *shape = Shape_MakePyramid(5);
|
||||
gs->shapes[0] = shape;
|
||||
@@ -256,27 +256,22 @@ bool Render_Init(GameState *gs)
|
||||
glGenBuffers(1, &shape->EBO);
|
||||
printf("Created shape 1.\n");
|
||||
|
||||
shape = Model_ReadObjFile("res/model/human.obj");
|
||||
gs->shapes[2] = shape;
|
||||
glGenVertexArrays(1, &shape->VAO);
|
||||
glGenBuffers(1, &shape->VBO);
|
||||
glGenBuffers(1, &shape->IBO);
|
||||
glGenBuffers(1, &shape->EBO);
|
||||
printf("Created shape 2.\n");
|
||||
|
||||
LoadTexture(gs);
|
||||
printf("Loaded textures.\n");
|
||||
Camera_Init(&gs->camera);
|
||||
|
||||
InitShapeBuffers(gs->shapes[0]);
|
||||
InitShapeBuffers(gs->shapes[1]);
|
||||
InitShapeBuffers(gs->shapes[2]);
|
||||
printf("Initialized buffers.\n");
|
||||
|
||||
gs->testModel = Model_LoadGltf("res/model/human.glb");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Render_Destroy(GameState *gs)
|
||||
{
|
||||
Model_Free(gs->testModel);
|
||||
SDL_GL_DeleteContext(gs->glContext);
|
||||
SDL_DestroyWindow(gs->window);
|
||||
}
|
||||
@@ -313,6 +308,132 @@ static void SetViewport(GameState *gs)
|
||||
Camera_GetViewMatrix(&gs->camera, gs->viewMatrix);
|
||||
}
|
||||
|
||||
static void Transform(ShapeInstance *instance, mat4 *matrix)
|
||||
{
|
||||
mat4 tempMat;
|
||||
glm_mat4_identity(tempMat);
|
||||
|
||||
glm_translate(tempMat, instance->position);
|
||||
|
||||
glm_rotate_x(tempMat, instance->rotation[0], tempMat);
|
||||
glm_rotate_y(tempMat, instance->rotation[1], tempMat);
|
||||
glm_rotate_z(tempMat, instance->rotation[2], tempMat);
|
||||
|
||||
vec3 scale;
|
||||
scale[0] = instance->scale;
|
||||
scale[1] = instance->scale;
|
||||
scale[2] = instance->scale;
|
||||
glm_scale(tempMat, scale);
|
||||
|
||||
memcpy(matrix, tempMat, sizeof(mat4));
|
||||
}
|
||||
|
||||
static void DrawModel(GameState *gs)
|
||||
{
|
||||
Model model = gs->testModel;
|
||||
ShapeInstance instance = model.instance;
|
||||
cgltf_data *data = model.data;
|
||||
cgltf_skin *skin = model.skin;
|
||||
cgltf_animation *anim = data->animations;
|
||||
mat4 modelMatrix;
|
||||
glm_mat4_identity(modelMatrix);
|
||||
Transform(&model.instance, &modelMatrix);
|
||||
|
||||
uint64_t ticks = SDL_GetTicks();
|
||||
float t = ticks / 1000.0f;
|
||||
while (t > 2.0f) t -= 2.0f;
|
||||
|
||||
// apply local transformations for each bone for the current animation frame
|
||||
for (int i = 0; i < anim->channels_count; i++)
|
||||
{
|
||||
cgltf_animation_channel* channel = &anim->channels[i];
|
||||
cgltf_animation_sampler* sampler = channel->sampler;
|
||||
cgltf_node* node = channel->target_node;
|
||||
|
||||
// find keyframe interval [k, k+1]
|
||||
size_t k = 0;
|
||||
float alpha = 0.0f;
|
||||
size_t num_keyframes = sampler->input->count;
|
||||
|
||||
// binary search may be faster
|
||||
for (size_t j = 0; j < num_keyframes - 1; ++j)
|
||||
{
|
||||
float t0, t1;
|
||||
cgltf_accessor_read_float(sampler->input, j, &t0, 1);
|
||||
cgltf_accessor_read_float(sampler->input, j + 1, &t1, 1);
|
||||
|
||||
if (t >= t0 && t <= t1)
|
||||
{
|
||||
k = j;
|
||||
float range = t1 - t0;
|
||||
alpha = (range > 0.0f) ? (t - t0) / range : 0.0f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// interpolate
|
||||
switch (channel->target_path)
|
||||
{
|
||||
case cgltf_animation_path_type_translation:
|
||||
{
|
||||
vec3 p0, p1, result;
|
||||
cgltf_accessor_read_float(sampler->output, k, p0, 3);
|
||||
cgltf_accessor_read_float(sampler->output, k + 1, p1, 3);
|
||||
glm_vec3_lerp(p0, p1, alpha, result);
|
||||
// glm_vec3_copy is not used here due to potential struct alignment issues
|
||||
for (int x = 0; x < 3; x++) node->translation[x] = result[x];
|
||||
node->has_translation = true;
|
||||
}
|
||||
break;
|
||||
case cgltf_animation_path_type_rotation:
|
||||
{
|
||||
versor q0, q1, result;
|
||||
cgltf_accessor_read_float(sampler->output, k, q0, 4);
|
||||
cgltf_accessor_read_float(sampler->output, k + 1, q1, 4);
|
||||
// slerp (not lerp) for rotation
|
||||
glm_quat_slerp(q0, q1, alpha, result);
|
||||
for (int x = 0; x < 4; x++) node->rotation[x] = result[x];
|
||||
node->has_rotation = true;
|
||||
}
|
||||
break;
|
||||
case cgltf_animation_path_type_scale:
|
||||
{
|
||||
vec3 s0, s1, result;
|
||||
cgltf_accessor_read_float(sampler->output, k, s0, 3);
|
||||
cgltf_accessor_read_float(sampler->output, k + 1, s1, 3);
|
||||
glm_vec3_lerp(s0, s1, alpha, result);
|
||||
for (int x = 0; x < 3; x++) node->scale[x] = result[x];
|
||||
node->has_scale = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// recalculate the joint matrices
|
||||
for (int i = 0; i < skin->joints_count; i++)
|
||||
{
|
||||
cgltf_node *joint = skin->joints[i];
|
||||
cgltf_node_transform_world(joint, (float*)model.jointMatrices[i]);
|
||||
mat4 inverseBind;
|
||||
cgltf_accessor_read_float(skin->inverse_bind_matrices, i, (float*)inverseBind, 16);
|
||||
glm_mat4_mul(model.jointMatrices[i], inverseBind, model.jointMatrices[i]);
|
||||
}
|
||||
|
||||
int shaderId = gs->modelShaderProgramId;
|
||||
glUseProgram(shaderId);
|
||||
glUniformMatrix4fv(glGetUniformLocation(shaderId, "theModelMatrix"), 1, GL_FALSE, (void*)(modelMatrix));
|
||||
glUniformMatrix4fv(glGetUniformLocation(shaderId, "theViewMatrix"), 1, GL_FALSE, (void*)(gs->viewMatrix));
|
||||
glUniformMatrix4fv(glGetUniformLocation(shaderId, "theProjMatrix"), 1, GL_FALSE, (void*)(gs->projMatrix));
|
||||
glUniformMatrix4fv(glGetUniformLocation(shaderId, "theJointMatrices"), 64, GL_FALSE, (void*)(model.jointMatrices));
|
||||
glUniform1f(glGetUniformLocation(shaderId, "texIndex"), 6.0f);
|
||||
glBindTexture(GL_TEXTURE_2D, gs->textureId);
|
||||
|
||||
glBindVertexArray(model.vao);
|
||||
glDrawElements(GL_TRIANGLES, model.indexCount, GL_UNSIGNED_SHORT, 0);
|
||||
}
|
||||
|
||||
void Render_Draw(GameState *gs)
|
||||
{
|
||||
glClearColor(0.4f, 0.6f, 0.8f, 1.0f);
|
||||
@@ -338,20 +459,7 @@ void Render_Draw(GameState *gs)
|
||||
{
|
||||
ShapeInstance* instance = shape->instances + j;
|
||||
mat4* matrix = (void*)(shape->instanceData + (j * 17) + 1);
|
||||
mat4 tempMat;
|
||||
glm_mat4_identity(tempMat);
|
||||
|
||||
glm_translate(tempMat, instance->position);
|
||||
glm_rotate_x(tempMat, instance->rotation[0], tempMat);
|
||||
glm_rotate_y(tempMat, instance->rotation[1], tempMat);
|
||||
glm_rotate_z(tempMat, instance->rotation[2], tempMat);
|
||||
|
||||
scale[0] = instance->scale;
|
||||
scale[1] = instance->scale;
|
||||
scale[2] = instance->scale;
|
||||
glm_scale(tempMat, scale);
|
||||
|
||||
memcpy(matrix, tempMat, sizeof(mat4));
|
||||
Transform(instance, matrix);
|
||||
}
|
||||
|
||||
// re-buffer the instance data because transformations may have changed
|
||||
@@ -361,5 +469,7 @@ void Render_Draw(GameState *gs)
|
||||
glDrawElementsInstanced(GL_TRIANGLES, shape->numIndices, GL_UNSIGNED_SHORT, 0, shape->numInstances);
|
||||
}
|
||||
|
||||
DrawModel(gs);
|
||||
|
||||
SDL_GL_SwapWindow(gs->window);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user