Animation blending
- transition smoothly between idle and run animations - use quaternions (versors) instead of yaw/pitch/roll - hide mouse and enable free-look mode by default on startup - rotate model 180 degrees in Blender so it faces away from the camera by default - add some makefile commands
This commit is contained in:
96
src/render.c
96
src/render.c
@@ -1,6 +1,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "SDL2/SDL.h"
|
||||
#include "GL/glew.h"
|
||||
#include "SDL2/SDL_opengl.h"
|
||||
@@ -220,6 +221,7 @@ bool Render_Init(GameState *gs)
|
||||
|
||||
gs->window = SDL_CreateWindow("Sandbox", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1920, 1080, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);;
|
||||
gs->glContext = SDL_GL_CreateContext(gs->window);
|
||||
SDL_SetRelativeMouseMode(SDL_TRUE);
|
||||
printf("Created OpenGL window.\n");
|
||||
|
||||
if (SDL_GL_SetSwapInterval(1) == 0) printf("Enabled VSync.\n");
|
||||
@@ -318,10 +320,7 @@ static void Transform(ShapeInstance *instance, mat4 *matrix)
|
||||
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);
|
||||
glm_quat_rotate(tempMat, instance->rotation, tempMat);
|
||||
|
||||
vec3 scale;
|
||||
scale[0] = instance->scale;
|
||||
@@ -332,43 +331,8 @@ static void Transform(ShapeInstance *instance, mat4 *matrix)
|
||||
memcpy(matrix, tempMat, sizeof(mat4));
|
||||
}
|
||||
|
||||
static void DrawModel(GameState *gs)
|
||||
static void ApplyAnim(cgltf_animation *anim, float t, float blend)
|
||||
{
|
||||
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);
|
||||
|
||||
if (!gs->input.freeLook)
|
||||
{
|
||||
// rotate the whole model to stay within this angle relative to the camera yaw
|
||||
const float turnThreshold = 1.047197f; // 60 degrees
|
||||
const float deg180 = 3.14159f;
|
||||
const float deg360 = 6.28318f;
|
||||
// convert camera yaw (rotation around Y axis) to radians and add 90 degrees so it aligns with the gltf model
|
||||
float camYawRadsGltf = (gs->camera.rotation[0] / -57.295828f) + 1.570795f;
|
||||
if (camYawRadsGltf > deg180) camYawRadsGltf = camYawRadsGltf - deg360;
|
||||
float *modelYaw = model->instance.rotation + 1;
|
||||
float diff = camYawRadsGltf - *modelYaw;
|
||||
if (diff > deg180) diff -= deg360;
|
||||
else if (diff < -deg180) diff += deg360;
|
||||
|
||||
if (turnThreshold < diff)
|
||||
*modelYaw = camYawRadsGltf - turnThreshold;
|
||||
else if (diff < -turnThreshold)
|
||||
*modelYaw = camYawRadsGltf + turnThreshold;
|
||||
}
|
||||
|
||||
Transform(&model->instance, &modelMatrix);
|
||||
|
||||
uint64_t ticks = SDL_GetTicks64();
|
||||
float t = ticks / 1000.0f;
|
||||
while (t > 1.0f) t -= 1.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];
|
||||
@@ -406,7 +370,9 @@ static void DrawModel(GameState *gs)
|
||||
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];
|
||||
memcpy(p0, node->translation, sizeof(vec3));
|
||||
glm_vec3_lerp(p0, result, blend, result);
|
||||
memcpy(node->translation, result, sizeof(vec3));
|
||||
node->has_translation = true;
|
||||
}
|
||||
break;
|
||||
@@ -417,7 +383,10 @@ static void DrawModel(GameState *gs)
|
||||
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];
|
||||
memcpy(q0, node->rotation, sizeof(versor));
|
||||
glm_quat_slerp(q0, result, blend, result);
|
||||
memcpy(node->rotation, result, sizeof(versor));
|
||||
|
||||
node->has_rotation = true;
|
||||
}
|
||||
break;
|
||||
@@ -427,7 +396,9 @@ static void DrawModel(GameState *gs)
|
||||
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];
|
||||
memcpy(s0, node->scale, sizeof(vec3));
|
||||
glm_vec3_lerp(s0, result, blend, result);
|
||||
memcpy(node->scale, result, sizeof(vec3));
|
||||
node->has_scale = true;
|
||||
}
|
||||
break;
|
||||
@@ -435,25 +406,40 @@ static void DrawModel(GameState *gs)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void DrawModel(GameState *gs)
|
||||
{
|
||||
Model *model = &gs->testModel;
|
||||
ShapeInstance *instance = &model->instance;
|
||||
cgltf_data *data = model->data;
|
||||
cgltf_skin *skin = model->skin;
|
||||
mat4 modelMatrix;
|
||||
glm_mat4_identity(modelMatrix);
|
||||
Transform(instance, &modelMatrix);
|
||||
|
||||
uint64_t ticks = SDL_GetTicks64();
|
||||
float t = ticks / 1000.0f;
|
||||
while (t > 1.0f) t -= 1.0f;
|
||||
|
||||
// apply local transformations for each bone for the current animation frame
|
||||
ApplyAnim(model->idle, t, 1.0f);
|
||||
ApplyAnim(model->run, t, gs->animBlend);
|
||||
|
||||
if (!gs->input.freeLook)
|
||||
{
|
||||
// make head look where camera is looking
|
||||
cgltf_node *head = model->head;
|
||||
vec3 a, b, yAxis;
|
||||
glm_vec3_zero(a);
|
||||
glm_vec3_zero(b);
|
||||
glm_vec3_zero(yAxis);
|
||||
yAxis[1] = 1.0f;
|
||||
a[2] = 1.0f;
|
||||
glm_vec3_rotate(a, model->instance.rotation[1], yAxis);
|
||||
b[0] = gs->camera.front[0];
|
||||
b[2] = gs->camera.front[2];
|
||||
vec3 modelFront, camFront;
|
||||
glm_quat_rotatev(instance->rotation, GLM_FORWARD, modelFront);
|
||||
glm_vec3_copy(gs->camera.front, camFront);
|
||||
modelFront[1] = 0.0f;
|
||||
camFront[1] = 0.0f;
|
||||
versor desiredRotation, temp;
|
||||
glm_quat_from_vecs(a, b, desiredRotation);
|
||||
for (int i = 0; i < 4; i++) temp[i] = head->rotation[i];
|
||||
glm_quat_from_vecs(modelFront, camFront, desiredRotation);
|
||||
memcpy(temp, head->rotation, sizeof(versor));
|
||||
glm_quat_mul(temp, desiredRotation, temp);
|
||||
for (int i = 0; i < 4; i++) head->rotation[i] = temp[i];
|
||||
memcpy(head->rotation, temp, sizeof(versor));
|
||||
}
|
||||
|
||||
// recalculate the joint matrices
|
||||
|
||||
Reference in New Issue
Block a user