Add skinning

This commit is contained in:
2026-03-05 14:19:46 +03:00
parent d57ac17b69
commit 65cf326f25
21 changed files with 1305 additions and 117 deletions

80
src/render/animation.cpp Normal file
View File

@@ -0,0 +1,80 @@
#include "animation.h"
#include "filesystem.h"
#include "model.h"
SkeletonInstance::SkeletonInstance() :
m_model(nullptr),
m_animation(INVALID_ANIMATION_HANDLE),
m_time(0.0f),
m_looped(false)
{
}
SkeletonInstance::~SkeletonInstance()
{
}
bool SkeletonInstance::IsValid()
{
return m_model != nullptr;
}
void SkeletonInstance::LoadAnimation(const char* filename)
{
if (!m_model)
return;
//m_model->LoadAnimation(filename);
}
AnimationId_t SkeletonInstance::FindAnimation(const char* name)
{
if (!m_model)
return INVALID_ANIMATION_HANDLE;
return m_model->FindAnimation(name);
}
uint32_t SkeletonInstance::GetNumAnimations()
{
if (!m_model)
return 0;
return m_model->GetNumAnimations();
}
float SkeletonInstance::GetAnimationTime(AnimationId_t id)
{
if (!m_model)
return 0.0f;
const Animation* animation = m_model->GetAnimation(id);
float frameTime = 1.0f / animation->framerate;
float totalDuration = animation->numFrames * frameTime;
return totalDuration;
}
void SkeletonInstance::PlayAnimation(AnimationId_t id, bool looped)
{
m_time = 0.0f;
m_animation = id;
m_looped = looped;
}
void SkeletonInstance::StopAnimation()
{
m_time = 0.0f;
m_animation = INVALID_ANIMATION_HANDLE;
m_looped = -1;
}
AnimationId_t SkeletonInstance::GetCurrentAnimation()
{
return m_animation;
}
float SkeletonInstance::GetCurrentTime()
{
return m_time;
}

68
src/render/animation.h Normal file
View File

@@ -0,0 +1,68 @@
#ifndef ANIMATION_H
#define ANIMATION_H
#include <vector>
#include "render_shared.h"
#define INVALID_ANIMATION_HANDLE -1
// Animation types
typedef /*int16_t*/ int AnimationId_t;
class Model;
/* Joint */
struct Joint
{
char name[64];
int parentId;
glm::vec3 origin;
glm::quat orient;
};
/* Animation */
struct Animation
{
char name[64];
uint32_t numPoses;
uint32_t numFrames;
float framerate;
std::vector< glm::vec3 > origin;
std::vector< glm::quat > orient;
};
/* Skeleton */
class SkeletonInstance
{
public:
Model* m_model;
std::vector<Joint> m_joints;
std::vector<glm::mat4> m_jointMatrices;
std::vector<glm::mat4> m_finalMatrices;
AnimationId_t m_animation;
float m_time;
bool m_looped;
public:
SkeletonInstance();
~SkeletonInstance();
bool IsValid();
void LoadAnimation(const char* filename);
AnimationId_t FindAnimation(const char* name);
uint32_t GetNumAnimations();
float GetAnimationTime(AnimationId_t id);
void PlayAnimation(AnimationId_t id, bool looped);
void StopAnimation();
AnimationId_t GetCurrentAnimation();
float GetCurrentTime();
};
#endif // !ANIMATION_H

134
src/render/iqm.h Normal file
View File

@@ -0,0 +1,134 @@
#ifndef __IQM_H__
#define __IQM_H__
#define IQM_MAGIC "INTERQUAKEMODEL"
#define IQM_VERSION 2
struct iqmheader
{
char magic[16];
unsigned int version;
unsigned int filesize;
unsigned int flags;
unsigned int num_text, ofs_text;
unsigned int num_meshes, ofs_meshes;
unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays;
unsigned int num_triangles, ofs_triangles, ofs_adjacency;
unsigned int num_joints, ofs_joints;
unsigned int num_poses, ofs_poses;
unsigned int num_anims, ofs_anims;
unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds;
unsigned int num_comment, ofs_comment;
unsigned int num_extensions, ofs_extensions;
};
struct iqmmesh
{
unsigned int name;
unsigned int material;
unsigned int first_vertex, num_vertexes;
unsigned int first_triangle, num_triangles;
};
enum
{
IQM_POSITION = 0,
IQM_TEXCOORD = 1,
IQM_NORMAL = 2,
IQM_TANGENT = 3,
IQM_BLENDINDEXES = 4,
IQM_BLENDWEIGHTS = 5,
IQM_COLOR = 6,
IQM_CUSTOM = 0x10
};
enum
{
IQM_BYTE = 0,
IQM_UBYTE = 1,
IQM_SHORT = 2,
IQM_USHORT = 3,
IQM_INT = 4,
IQM_UINT = 5,
IQM_HALF = 6,
IQM_FLOAT = 7,
IQM_DOUBLE = 8
};
struct iqmtriangle
{
unsigned int vertex[3];
};
struct iqmadjacency
{
unsigned int triangle[3];
};
struct iqmjointv1
{
unsigned int name;
int parent;
float translate[3], rotate[3], scale[3];
};
struct iqmjoint
{
unsigned int name;
int parent;
float translate[3], rotate[4], scale[3];
};
struct iqmposev1
{
int parent;
unsigned int mask;
float channeloffset[9];
float channelscale[9];
};
struct iqmpose
{
int parent;
unsigned int mask;
float channeloffset[10];
float channelscale[10];
};
struct iqmanim
{
unsigned int name;
unsigned int first_frame, num_frames;
float framerate;
unsigned int flags;
};
enum
{
IQM_LOOP = 1<<0
};
struct iqmvertexarray
{
unsigned int type;
unsigned int flags;
unsigned int format;
unsigned int size;
unsigned int offset;
};
struct iqmbounds
{
float bbmin[3], bbmax[3];
float xyradius, radius;
};
struct iqmextension
{
unsigned int name;
unsigned int num_data, ofs_data;
unsigned int ofs_extensions; // pointer to next extension
};
#endif

View File

@@ -2,6 +2,7 @@
#include <vector>
#include <string>
#include "ifilesystem.h"
#include "core.h"
#include "log.h"
#include "render.h"
#include "gpu_buffer.h"
@@ -11,8 +12,11 @@
#include "model.h"
#include "modelsystem.h"
#include "texturesmanager.h"
#include "iqm.h"
#include "debugrender.h"
extern Shader* g_unlitShader;
extern Shader* g_unlitSkinnedShader;
static std::string getFileNameWithoutExtension(const std::string& filename)
{
@@ -26,9 +30,8 @@ static std::string getFileNameWithoutExtension(const std::string& filename)
Model::Model()
{
m_data.vb = nullptr;
m_data.ib = nullptr;
m_AlbedoTexture = nullptr;
m_numPoses = 0;
//m_boundingBox.min = glm::vec3(0.0f);
//m_boundingBox.max = glm::vec3(0.0f);
@@ -38,11 +41,382 @@ Model::~Model()
{
m_AlbedoTexture = nullptr;
if (m_data.vb)
for (int i = 0; i < m_meshes.size(); i++)
ReleaseModelData(m_meshes[i]);
}
// IQM
typedef unsigned char byte;
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned int uint;
typedef signed long long int llong;
typedef unsigned long long int ullong;
inline iqmheader* LoadIQMHeader(const char* filename)
{
FileHandle_t handle = GetFileSystem()->OpenFile(filename, "rb");
SDL_assert(handle != kInvalidFileHandleValue);
size_t size = GetFileSystem()->GetFileLength(handle);
char* buffer = (char*)malloc(size);
GetFileSystem()->ReadFile(handle, buffer, size);
GetFileSystem()->CloseFile(handle);
return (iqmheader*)buffer;
}
inline void* IQMHeaderPtr(iqmheader* pHeader, uint offset)
{
return ((byte*)pHeader) + offset;
}
static bool g_bIsLittleEndian = false;
static void EndianSwap_Init()
{
union
{
delete m_data.vb;
m_data.vb = nullptr;
int i;
uchar b[sizeof(int)];
} conv;
conv.i = 1;
g_bIsLittleEndian = conv.b[0] != 0;
}
static inline bool islittleendian() { union { int i; uchar b[sizeof(int)]; } conv; conv.i = 1; return conv.b[0] != 0; }
inline ushort endianswap16(ushort n) { return (n << 8) | (n >> 8); }
inline uint endianswap32(uint n) { return (n << 24) | (n >> 24) | ((n >> 8) & 0xFF00) | ((n << 8) & 0xFF0000); }
template<class T> inline T endianswap(T n) { union { T t; uint i; } conv; conv.t = n; conv.i = endianswap32(conv.i); return conv.t; }
template<> inline ushort endianswap<ushort>(ushort n) { return endianswap16(n); }
template<> inline short endianswap<short>(short n) { return endianswap16(n); }
template<> inline uint endianswap<uint>(uint n) { return endianswap32(n); }
template<> inline int endianswap<int>(int n) { return endianswap32(n); }
template<class T> inline void endianswap(T* buf, int len) { for (T* end = &buf[len]; buf < end; buf++) *buf = endianswap(*buf); }
template<class T> inline T lilswap(T n) { return islittleendian() ? n : endianswap(n); }
template<class T> inline void lilswap(T* buf, int len) { if (!islittleendian()) endianswap(buf, len); }
template<class T> inline T bigswap(T n) { return islittleendian() ? endianswap(n) : n; }
template<class T> inline void bigswap(T* buf, int len) { if (islittleendian()) endianswap(buf, len); }
template<class T> T getval(FILE* f) { T n; return fread(&n, 1, sizeof(n), f) == sizeof(n) ? n : 0; }
template<class T> T getlil(FILE* f) { return lilswap(getval<T>(f)); }
template<class T> T getbig(FILE* f) { return bigswap(getval<T>(f)); }
void Model::LoadIqm(const char* filename)
{
iqmheader* pHdr = LoadIQMHeader(filename);
pHdr->version = lilswap(pHdr->version);
pHdr->filesize = lilswap(pHdr->filesize);
pHdr->num_vertexarrays = lilswap(pHdr->num_vertexarrays);
pHdr->num_vertexes = lilswap(pHdr->num_vertexes);
pHdr->num_triangles = lilswap(pHdr->num_triangles);
pHdr->num_meshes = lilswap(pHdr->num_meshes);
pHdr->num_joints = lilswap(pHdr->num_joints);
pHdr->num_poses = lilswap(pHdr->num_poses);
pHdr->num_anims = lilswap(pHdr->num_anims);
pHdr->num_frames = lilswap(pHdr->num_frames);
pHdr->num_framechannels = lilswap(pHdr->num_framechannels);
pHdr->ofs_vertexarrays = lilswap(pHdr->ofs_vertexarrays);
pHdr->ofs_triangles = lilswap(pHdr->ofs_triangles);
pHdr->ofs_meshes = lilswap(pHdr->ofs_meshes);
pHdr->ofs_joints = lilswap(pHdr->ofs_joints);
pHdr->ofs_poses = lilswap(pHdr->ofs_poses);
pHdr->ofs_anims = lilswap(pHdr->ofs_anims);
pHdr->ofs_frames = lilswap(pHdr->ofs_frames);
m_numPoses = pHdr->num_poses;
if (pHdr->version != IQM_VERSION)
{
Msg("Model::LoadIQM: %s is outdated. current is %i, model %i", filename, IQM_VERSION, pHdr->version);
free(pHdr);
return;
}
if (pHdr->filesize > (16 << 20))
{
Msg("Model::LoadIQM: %s is bigger(%i) than 16 MB!", filename, pHdr->filesize);
free(pHdr);
return;
}
iqmvertexarray* pVertexArrays = (iqmvertexarray*)IQMHeaderPtr(pHdr, pHdr->ofs_vertexarrays);
float* position = nullptr;
float* normal = nullptr;
float* texcoord = nullptr;
byte* weight = nullptr;
byte* boneid = nullptr;
for (uint32_t i = 0; i < pHdr->num_vertexarrays; i++)
{
const iqmvertexarray& vertarray = pVertexArrays[i];
if (vertarray.type == IQM_POSITION)
{
if (vertarray.format != IQM_FLOAT || vertarray.size != 3)
Core::Error("Model::LoadIQM: %s wrong model! position attribute are not vec3 and float.", filename);
position = (float*)IQMHeaderPtr(pHdr, vertarray.offset);
}
else if (vertarray.type == IQM_NORMAL)
{
if (vertarray.format != IQM_FLOAT || vertarray.size != 3)
Core::Error("Model::LoadIQM: %s wrong model! normal attribute are not vec3 and float.", filename);
normal = (float*)IQMHeaderPtr(pHdr, vertarray.offset);
}
else if (vertarray.type == IQM_TEXCOORD)
{
if (vertarray.format != IQM_FLOAT || vertarray.size != 2)
Core::Error("Model::LoadIQM: %s wrong model! texcoord attribute are not vec2 and float.", filename);
texcoord = (float*)IQMHeaderPtr(pHdr, vertarray.offset);
}
else if (vertarray.type == IQM_BLENDWEIGHTS)
{
if (vertarray.format != IQM_UBYTE || vertarray.size != 4)
Core::Error("Model::LoadIQM: %s wrong model! bone weights attribute are not vec4 and ubyte.", filename);
weight = (byte*)IQMHeaderPtr(pHdr, vertarray.offset);
}
else if (vertarray.type == IQM_BLENDINDEXES)
{
if (vertarray.format != IQM_UBYTE || vertarray.size != 4)
Core::Error("Model::LoadIQM: %s wrong model! bone indices attribute are not vec4 and ubyte.", filename);
boneid = (byte*)IQMHeaderPtr(pHdr, vertarray.offset);
}
}
std::vector< SkinnedMeshVertex > vertices;
vertices.resize(pHdr->num_vertexes);
std::vector< uint > indices;
indices.resize(pHdr->num_triangles * 3);
iqmmesh* pMeshes = (iqmmesh*)IQMHeaderPtr(pHdr, pHdr->ofs_meshes);
iqmtriangle* pTriangles = (iqmtriangle*)IQMHeaderPtr(pHdr, pHdr->ofs_triangles);
// Material* pMaterial = nullptr;
bool firstMesh = true;
for (int i = 0; i < pHdr->num_meshes; ++i)
{
iqmmesh* pMesh = &pMeshes[i];
for (uint32_t j = 0; j < pMesh->num_vertexes; ++j)
{
memcpy(&vertices[j].position, &position[(pMesh->first_vertex + j) * 3], sizeof(vertices[j].position));
memcpy(&vertices[j].normal, &normal[(pMesh->first_vertex + j) * 3], sizeof(vertices[j].normal));
memcpy(&vertices[j].texcoord, &texcoord[(pMesh->first_vertex + j) * 2], sizeof(vertices[j].texcoord));
if (boneid)
{
vertices[j].boneIds.x = (float)boneid[(pMesh->first_vertex + j) * 4];
vertices[j].boneIds.y = (float)boneid[(pMesh->first_vertex + j) * 4 + 1];
vertices[j].boneIds.z = (float)boneid[(pMesh->first_vertex + j) * 4 + 2];
vertices[j].boneIds.w = (float)boneid[(pMesh->first_vertex + j) * 4 + 3];
}
else
{
vertices[j].boneIds.x = 0.0f;
vertices[j].boneIds.y = 0.0f;
vertices[j].boneIds.z = 0.0f;
vertices[j].boneIds.w = 0.0f;
}
if (weight)
{
vertices[j].weights.x = weight[(pMesh->first_vertex + j) * 4] / 255.0f;
vertices[j].weights.y = weight[(pMesh->first_vertex + j) * 4 + 1] / 255.0f;
vertices[j].weights.z = weight[(pMesh->first_vertex + j) * 4 + 2] / 255.0f;
vertices[j].weights.w = weight[(pMesh->first_vertex + j) * 4 + 3] / 255.0f;
}
else
{
vertices[j].weights.x = 0.0f;
vertices[j].weights.y = 0.0f;
vertices[j].weights.z = 0.0f;
vertices[j].weights.w = 0.0f;
}
if (i == 0 && (firstMesh && j == 0))
{
m_boundingBox.m_min = vertices[j].position;
m_boundingBox.m_max= vertices[j].position;
}
else
{
m_boundingBox.m_min = glm::min(m_boundingBox.m_min, vertices[j].position);
m_boundingBox.m_max = glm::max(m_boundingBox.m_max, vertices[j].position);
}
}
// Re-order triangle face counting to counter-clockwise
for (uint j = 0; j < pMesh->num_triangles; j++)
{
uint a = pTriangles[pMesh->first_triangle + j].vertex[0];
uint b = pTriangles[pMesh->first_triangle + j].vertex[1];
uint c = pTriangles[pMesh->first_triangle + j].vertex[2];
indices[j * 3 + 0] = c - pMesh->first_vertex;
indices[j * 3 + 1] = b - pMesh->first_vertex;
indices[j * 3 + 2] = a - pMesh->first_vertex;
}
if (pHdr->ofs_text)
{
// #TODO: weird weird getting material name string is so wrong
const char* str = (char*)pHdr + pHdr->ofs_text;
const char* materialname = &str[pMesh->material];
//if (materialname)
// pMaterial = g_materialSystem.LoadMaterial(materialname);
}
ModelData_t mesh = {};
//mesh.m_vertices = vertices;
//mesh.m_indices = indices;
mesh.vb = g_renderDevice->CreateVertexBuffer(vertices.data(), pMesh->num_vertexes * sizeof(SkinnedMeshVertex), true);
mesh.vbcount = pMesh->num_vertexes;
mesh.ib = g_renderDevice->CreateIndexBuffer(indices.data(), pMesh->num_triangles * sizeof(iqmtriangle), false);
mesh.ibcount = pMesh->num_triangles * 3;
//mesh.m_material = pMaterial;
//
//if (!mesh.m_material)
// mesh.m_material = g_materialSystem.CreateMaterial("default_model", "textures/system/notex.tga", NULL, true);
m_meshes.push_back(mesh);
firstMesh = false;
}
// get string data
char* pStrings = (char*)IQMHeaderPtr(pHdr, pHdr->ofs_text);
// load joints
iqmjoint* pJoints = (iqmjoint*)IQMHeaderPtr(pHdr, pHdr->ofs_joints);
m_joints.resize(pHdr->num_joints);
for (uint32_t i = 0; i < pHdr->num_joints; i++)
{
const char* jointName = &pStrings[pJoints[i].name];
strncpy(m_joints[i].name, jointName, sizeof(m_joints[i].name));
m_joints[i].parentId = pJoints[i].parent;
memcpy(&m_joints[i].origin, pJoints[i].translate, sizeof(glm::vec3));
memcpy(&m_joints[i].orient, pJoints[i].rotate, sizeof(glm::vec4));
m_joints[i].orient = normalize(m_joints[i].orient);
//Msg("%i origin %.2f %.2f %.2f, orient %.2f %.2f %.2f %.2f(w)",
// i,
// m_joints[i].origin.x, m_joints[i].origin.y, m_joints[i].origin.z,
// m_joints[i].orient.x, m_joints[i].orient.y, m_joints[i].orient.z, m_joints[i].orient.w);
}
m_basePose = m_joints;
m_inverseBasePose.resize(m_basePose.size());
for (int i = 0; i < m_basePose.size(); i++)
{
// calculate inverse transform matrix
glm::mat4 mat = glm::translate(glm::mat4(1.0f), m_basePose[i].origin);
mat *= glm::toMat4(m_basePose[i].orient);
m_inverseBasePose[i] = glm::inverse(mat);
if (m_basePose[i].parentId >= 0)
{
m_inverseBasePose[i] *= m_inverseBasePose[m_basePose[i].parentId];
}
}
// load animations
iqmanim* pAnims = (iqmanim*)IQMHeaderPtr(pHdr, pHdr->ofs_anims);
iqmpose* pPoses = (iqmpose*)IQMHeaderPtr(pHdr, pHdr->ofs_poses);
ushort* pFramedata = (ushort*)IQMHeaderPtr(pHdr, pHdr->ofs_frames);
// #TODO: tracks are Array class, with resize no ctor are called
// m_animations.resize(pHdr->num_anims);
//m_jointTranslation.resize(pHdr->num_frames* pHdr->num_poses);
glm::vec3 origin;
glm::vec3 scale;
glm::quat orient;
for (uint32_t i = 0; i < pHdr->num_anims; i++)
{
Animation animation;
const char* animName = &pStrings[pAnims[i].name];
strncpy(animation.name, animName, sizeof(animation.name));
animation.numPoses = m_numPoses;
animation.numFrames = pAnims[i].num_frames;
animation.framerate = pAnims[i].framerate;
if (animation.framerate <= 0.0f)
animation.framerate = 30.0f;
Msg("Animation: %s %i %i %i", animName, pAnims[i].first_frame, pAnims[i].num_frames, pAnims[i].framerate);
animation.origin.resize(pHdr->num_poses * pAnims[i].num_frames);
animation.orient.resize(pHdr->num_poses * pAnims[i].num_frames);
int dcounter = pAnims[i].first_frame * pHdr->num_framechannels;
for (uint32_t j = 0; j < pAnims[i].num_frames; j++)
{
for (uint32_t k = 0; k < pHdr->num_poses; k++)
{
const iqmpose& p = pPoses[k];
origin.x = p.channeloffset[0]; if (p.mask & 0x01) origin.x += *pFramedata++ * p.channelscale[0];
origin.y = p.channeloffset[1]; if (p.mask & 0x02) origin.y += *pFramedata++ * p.channelscale[1];
origin.z = p.channeloffset[2]; if (p.mask & 0x04) origin.z += *pFramedata++ * p.channelscale[2];
orient.x = p.channeloffset[3]; if (p.mask & 0x08) orient.x += *pFramedata++ * p.channelscale[3];
orient.y = p.channeloffset[4]; if (p.mask & 0x10) orient.y += *pFramedata++ * p.channelscale[4];
orient.z = p.channeloffset[5]; if (p.mask & 0x20) orient.z += *pFramedata++ * p.channelscale[5];
orient.w = p.channeloffset[6]; if (p.mask & 0x40) orient.w += *pFramedata++ * p.channelscale[6];
scale.x = p.channeloffset[7]; if (p.mask & 0x80) scale.x += *pFramedata++ * p.channelscale[7];
scale.y = p.channeloffset[8]; if (p.mask & 0x100) scale.y += *pFramedata++ * p.channelscale[8];
scale.z = p.channeloffset[9]; if (p.mask & 0x200) scale.z += *pFramedata++ * p.channelscale[9];
// normalize orientation
orient = glm::normalize(orient);
// assign to the animation track
animation.origin[j * pHdr->num_poses + k] = origin;
animation.orient[j * pHdr->num_poses + k] = orient;
}
}
m_animations.push_back(animation);
}
m_jointMatrices.resize(m_joints.size());
m_finalMatrices.resize(m_joints.size());
// matrices
for (int i = 0; i < m_joints.size(); ++i)
{
glm::mat4 mat = glm::translate(glm::mat4(1.0f), m_joints[i].origin);
mat *= glm::toMat4(m_joints[i].orient);
if (m_joints[i].parentId >= 0)
m_jointMatrices[i] = m_jointMatrices[m_joints[i].parentId] * mat;
else
m_jointMatrices[i] = mat;
m_finalMatrices[i] = m_jointMatrices[i] * m_inverseBasePose[i];
}
free(pHdr);
}
void Model::LoadObj(const char* filename)
@@ -168,8 +542,10 @@ void Model::LoadObj(const char* filename)
m_Vertices = vertices;
ModelData_t m_data = {};
m_data.vb = g_renderDevice->CreateVertexBuffer(vertices.data(), (int)sizeof(StaticMeshVertex) * (int)vertices.size());
m_data.vbcount = vertices.size();
m_meshes.push_back(m_data);
std::string mtlfilename = getFileNameWithoutExtension(filename);
mtlfilename += ".mtl";
@@ -218,86 +594,157 @@ void Model::LoadMtl(const char* filename)
fclose(file);
}
void Model::Draw(const glm::mat4& model, bool isTransparent /*= false*/)
void Model::Draw(const glm::mat4& model, SkeletonInstance* instance /*= nullptr*/)
{
SDL_assert(g_unlitShader);
glFrontFace(GL_CCW);
glDepthFunc(GL_LESS);
Shader* shader = instance ? g_unlitSkinnedShader : g_unlitShader;
g_renderDevice->SetCullFace(true);
g_renderDevice->SetDepthTest(true);
g_renderDevice->SetDepthWrite(true);
if (isTransparent)
for (int i = 0; i < m_meshes.size(); i++)
{
// Enable blending
g_renderDevice->SetBlending(true);
g_renderDevice->SetBlendingFunction(BF_SRC_ALPHA, BF_ONE_MINUS_SRC_ALPHA);
ModelData_t& m_data = m_meshes[i];
glm::vec4 color = glm::vec4(1.f, 1.f, 1.f, .5f);
g_shaderSystem->SetUniformFloat4(g_unlitShader, UNIFORM_CUSTOM_COLOR, glm::value_ptr(color));
}
else
{
g_renderDevice->SetBlending(false);
glFrontFace(GL_CCW);
glDepthFunc(GL_LESS);
//glm::vec4 color = glm::vec4(1.f, 1.f, 1.f, 1.f);
//g_shaderSystem->SetUniformFloat4(g_unlitShader, UNIFORM_CUSTOM_COLOR, glm::value_ptr(color));
g_renderDevice->SetCullFace(true);
g_renderDevice->SetDepthTest(true);
{
if (m_data.ib)
g_renderDevice->DrawElements(PT_TRIANGLES, m_data.ibcount, 0, NULL);
glm::mat4 worldMat;
glm::mat4 worldMatParent;
for (int i = 0; i < instance->m_jointMatrices.size(); i++)
{
worldMat = model * instance->m_jointMatrices[i];
glm::vec3 worldPos = worldMat[3];
// g_debugRender->DrawAxis(worldPos);
const Joint& joint = instance->m_joints[i];
if (joint.parentId != -1)
{
worldMatParent = model * instance->m_jointMatrices[joint.parentId];
g_debugRender->DrawLine(worldMat[3], worldMatParent[3], glm::vec3(1.0f, 1.0f, 1.0f));
}
}
}
g_renderDevice->SetVerticesBuffer(m_data.vb);
}
g_shaderSystem->SetShader(g_unlitShader);
g_shaderSystem->SetUniformMatrix(g_unlitShader, UNIFORM_MODEL_MATRIX, &model[0]);
AnimationId_t Model::FindAnimation(const char* name)
{
for (int i = 0, numAnims = GetNumAnimations(); i < numAnims; i++)
{
if (strcmp(m_animations[i].name, name) == 0)
return i;
}
//static float test = 0.0f;
//test += g_systemTimer->GetDelta() * 6.0f;
return -1;
}
//glm::mat4 model = glm::mat4(1.0f);
uint32_t Model::GetNumAnimations()
{
return (uint32_t)m_animations.size();
}
//int32_t x = 0, y = 0;
//g_inputSystem->GetMousePosition(&x, &y);
//
//glm::mat4 model = glm::mat4(1.0f);
//model = glm::rotate(model, test, glm::vec3(0.0f, 1.0f, 0.0f));
//
//float realY = (float)g_renderView.height - (float)y - 1;
//
//glm::vec4 viewport = glm::vec4(0.0f, 0.0f, (float)g_renderView.width, (float)g_renderView.height);
//glm::vec3 pos = glm::unProject(
// glm::vec3((float)x, realY, 0.0f),
// model,
// g_renderView.proj,
// viewport);
//
//pos *= 50.0f;
//
//model = glm::translate(model, pos);
const Animation* Model::GetAnimation(AnimationId_t index)
{
if (-1 >= index || index >= GetNumAnimations())
Core::Error("Model::GetAnimation: index %d is out of bounds.", index);
glm::mat4 mvp = glm::identity<glm::mat4>();
mvp = g_render->GetProjectionMatrix() * g_render->GetViewMatrix() * model;
g_shaderSystem->SetUniformMatrix(g_unlitShader, UNIFORM_MVP_MATRIX, &mvp[0]);
return &m_animations[index];
}
g_texturesManager->SetTexture(0, m_AlbedoTexture);
g_shaderSystem->SetUniformSampler(g_unlitShader, SAMPLER_ALBEDO, 0);
int Model::GetNumPoses()
{
return m_numPoses;
}
g_renderDevice->DrawArrays(PT_TRIANGLES, 0, m_data.vbcount);
SkeletonInstance* Model::CreateSkeletonInstance()
{
SkeletonInstance* instance = new SkeletonInstance();
instance->m_model = this;
instance->m_joints = m_joints;
instance->m_jointMatrices.resize(m_joints.size());
instance->m_finalMatrices.resize(m_joints.size());
instance->m_animation = INVALID_ANIMATION_HANDLE;
instance->m_time = 0.0;
instance->m_looped = false;
#if 0
glm::mat4 mvp = glm::identity<glm::mat4>();
mvp = g_renderView.proj * g_renderView.view * model;
g_shaderSystem->SetUniformMatrix(g_litShader, UNIFORM_MVP_MATRIX, &mvp[0]);
for (int i = 0; i < m_joints.size(); ++i)
{
glm::mat4 mat = glm::translate(glm::mat4(1.0f), m_joints[i].origin);
mat *= glm::toMat4(m_joints[i].orient);
g_texturesManager->SetTexture(0, m_AlbedoTexture);
g_shaderSystem->SetUniformSampler(g_litShader, SAMPLER_ALBEDO, 0);
if (m_joints[i].parentId >= 0)
instance->m_jointMatrices[i] = instance->m_jointMatrices[m_joints[i].parentId] * mat;
else
instance->m_jointMatrices[i] = mat;
g_renderDevice->DrawArrays(PT_TRIANGLES, 0, m_data.vbcount);
instance->m_finalMatrices[i] = instance->m_jointMatrices[i] * m_inverseBasePose[i];
}
return instance;
}
void Model::UpdateSkeletonInstance(SkeletonInstance* instance, float dt)
{
if (!instance)
return;
if (instance->m_animation == -1)
return;
const Animation& animation = m_animations[instance->m_animation];
instance->m_time += dt;
float frameTime = 1.0f / animation.framerate;
float totalDuration = animation.numFrames * frameTime;
if (instance->m_time >= totalDuration && instance->m_looped)
instance->m_time = 0.0f;
float animationFrame = instance->m_time / frameTime;
int frameA = (int)(floor(animationFrame));
int frameB = (frameA + 1) % animation.numFrames;
if (!instance->m_looped && frameA >= animation.numFrames - 1)
frameA = animation.numFrames - 1;
float t = animationFrame - frameA;
for (int i = 0; i < instance->m_joints.size(); ++i)
{
const glm::vec3& a = animation.origin[frameA * m_numPoses + i];
const glm::vec3& b = animation.origin[frameB * m_numPoses + i];
const glm::quat& qa = animation.orient[frameA * m_numPoses + i];
const glm::quat& qb = animation.orient[frameB * m_numPoses + i];
instance->m_joints[i].origin = glm::lerp(a, b, t);
instance->m_joints[i].orient = glm::normalize(glm::slerp(qa, qb, t));
}
// matrices
for (int i = 0; i < instance->m_joints.size(); ++i)
{
glm::mat4 mat = glm::translate(glm::mat4(1.0f), instance->m_joints[i].origin);
mat *= glm::toMat4(instance->m_joints[i].orient);
if (m_joints[i].parentId >= 0)
instance->m_jointMatrices[i] = instance->m_jointMatrices[m_joints[i].parentId] * mat;
else
instance->m_jointMatrices[i] = mat;
instance->m_finalMatrices[i] = instance->m_jointMatrices[i] * m_inverseBasePose[i];
}
BoundingBox bbox = m_boundingBox;
TransformBoundingBox(bbox, model);
g_debugRender->DrawBoundingBox(bbox, glm::vec3(1.0f));
#endif
}
void ReleaseModelData(ModelData_t& data)

View File

@@ -5,6 +5,7 @@
#include "boundingbox.h"
#include "render_shared.h"
#include "animation.h"
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
@@ -31,18 +32,46 @@ public:
Model();
~Model();
void LoadIqm(const char* filename);
void LoadObj(const char* filename);
void LoadMtl(const char* filename);
void Draw(const glm::mat4& model, bool isTransparent = false);
void Draw(const glm::mat4& model, SkeletonInstance* instance = nullptr);
const BoundingBox& GetBoundingBox() { return m_boundingBox; }
// Animation
AnimationId_t FindAnimation(const char* name);
uint32_t GetNumAnimations();
const Animation* GetAnimation(AnimationId_t index);
int GetNumPoses();
SkeletonInstance* CreateSkeletonInstance();
void UpdateSkeletonInstance(SkeletonInstance* instance, float dt);
// void PlayAnimation(AnimationId_t id, bool looped);
// void StopAnimation();
private:
std::vector<Joint> m_joints;
std::vector<Joint> m_basePose;
std::vector<glm::mat4> m_inverseBasePose;
std::vector<glm::mat4> m_jointMatrices;
std::vector<glm::mat4> m_finalMatrices;
std::vector<Animation> m_animations;
std::vector<StaticMeshVertex> m_Vertices;
ModelData_t m_data;
std::vector<ModelData_t> m_meshes;
BoundingBox m_boundingBox;
Texture2D* m_AlbedoTexture;
int m_numPoses;
};
#endif // !MODEL_H

View File

@@ -23,11 +23,15 @@ static InputLayoutDesc_t g_staticVertexLayout[] = {
static InputLayoutDesc_t g_skinnedVertexLayout[] = {
{ VERTEXATTR_VEC3, SHADERSEMANTIC_POSITION },
{ VERTEXATTR_VEC3, SHADERSEMANTIC_NORMAL },
{ VERTEXATTR_VEC2, SHADERSEMANTIC_TEXCOORD }
{ VERTEXATTR_VEC2, SHADERSEMANTIC_TEXCOORD },
{ VERTEXATTR_VEC4, SHADERSEMANTIC_BONEIDS },
{ VERTEXATTR_VEC4, SHADERSEMANTIC_WEIGHTS },
};
Shader* g_unlitShader = nullptr;
Shader* g_litShader = nullptr;
Shader* g_unlitSkinnedShader = nullptr;
Shader* g_litSkinnedShader = nullptr;
static std::string getFileNameWithoutExtension(const std::string& filename)
{
@@ -56,6 +60,10 @@ void ModelSystem::Init()
// Load lighted model generic shader
g_litShader = g_shaderSystem->CreateShader("lit_generic", "data/shaders/lit_generic.vs", "data/shaders/lit_generic.ps",
g_staticVertexLayout, sizeof(g_staticVertexLayout) / sizeof(g_staticVertexLayout[0]));
// Load unlighted skinned model generic shader
g_unlitSkinnedShader = g_shaderSystem->CreateShader("unlit_generic_skin", "data/shaders/unlit_generic_skin.vs", "data/shaders/unlit_generic.ps",
g_skinnedVertexLayout, sizeof(g_skinnedVertexLayout) / sizeof(g_skinnedVertexLayout[0]));
}
void ModelSystem::Shutdown()
@@ -92,6 +100,10 @@ Model* ModelSystem::LoadModel(const char* filename)
{
model->LoadObj(filename);
}
else if (strstr(filename, ".iqm"))
{
model->LoadIqm(filename);
}
else
{
// Find file extension

View File

@@ -5,6 +5,8 @@
#include <stdint.h>
#include <glm/glm.hpp>
#include <glm/gtx/quaternion.hpp>
#include <glm/gtx/compatibility.hpp>
typedef uint32_t index_t;
@@ -207,6 +209,7 @@ enum ShaderUniform_t
UNIFORM_SUN_COLOR,
UNIFORM_SUN_AMBIENT,
UNIFORM_CAMERA_POS,
UNIFORM_BONE_MATRICES,
UNIFORM_MAX,
};
@@ -238,4 +241,13 @@ struct StaticMeshVertex
glm::vec2 texcoord;
};
struct SkinnedMeshVertex
{
glm::vec3 position;
glm::vec3 normal;
glm::vec2 texcoord;
glm::vec4 boneIds;
glm::vec4 weights;
};
#endif

View File

@@ -13,6 +13,7 @@ static const char* g_uniformNameTable[UNIFORM_MAX] =
"u_sunColor",
"u_sunAmbientColor",
"u_cameraPos",
"u_boneMatrices[0]"
};
static const char* g_samplersNameTable[SAMPLER_MAX] =
@@ -152,9 +153,9 @@ void ShaderSystem::SetUniformFloat4(const Shader* shader, ShaderUniform_t unifor
glUniform4fv( shader->m_glUniformLocation[ uniform ], 1, ( const GLfloat* )data );
}
void ShaderSystem::SetUniformMatrix( const Shader* shader, ShaderUniform_t uniform, const void* data )
void ShaderSystem::SetUniformMatrix( const Shader* shader, ShaderUniform_t uniform, const void* data, int numMatrices /*= 0*/ )
{
glUniformMatrix4fv( shader->m_glUniformLocation[ uniform ], 1, GL_FALSE, ( const GLfloat* )data );
glUniformMatrix4fv( shader->m_glUniformLocation[ uniform ], numMatrices, GL_FALSE, ( const GLfloat* )data );
}
void Shader::AllocateAttributes()

View File

@@ -21,7 +21,7 @@ public:
void SetUniformSampler( const Shader* shader, ShaderSamplers_t sampler, int index );
void SetUniformFloat4( const Shader* shader, ShaderUniform_t uniform, const void* data );
void SetUniformMatrix( const Shader* shader, ShaderUniform_t uniform, const void* data );
void SetUniformMatrix( const Shader* shader, ShaderUniform_t uniform, const void* data, int numMatrices = 1 );
private:
std::vector<Shader*> m_shaders;