880 lines
26 KiB
C++
880 lines
26 KiB
C++
#include <assert.h>
|
|
#include <vector>
|
|
#include <string>
|
|
#include "ifilesystem.h"
|
|
#include "core.h"
|
|
#include "log.h"
|
|
#include "render.h"
|
|
#include "gpu_buffer.h"
|
|
#include "shader.h"
|
|
#include "shadersystem.h"
|
|
#include "renderdevice.h"
|
|
#include "model.h"
|
|
#include "modelsystem.h"
|
|
#include "texturesmanager.h"
|
|
#include "iqm.h"
|
|
#include "debugrender.h"
|
|
#include "scenemanager.h"
|
|
#include "camera.h"
|
|
|
|
extern Shader* g_unlitShader;
|
|
extern Shader* g_unlitSkinnedShader;
|
|
extern Shader* g_litShader;
|
|
extern Shader* g_litSkinnedShader;
|
|
|
|
static std::string getFileNameWithoutExtension(const std::string& filename)
|
|
{
|
|
size_t lastindex = filename.find_last_of(".");
|
|
if (lastindex != std::string::npos) {
|
|
return filename.substr(0, lastindex);
|
|
}
|
|
|
|
return filename;
|
|
}
|
|
|
|
Model::Model()
|
|
{
|
|
m_numPoses = 0;
|
|
|
|
//m_boundingBox.min = glm::vec3(0.0f);
|
|
//m_boundingBox.max = glm::vec3(0.0f);
|
|
}
|
|
|
|
Model::~Model()
|
|
{
|
|
|
|
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
|
|
{
|
|
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;
|
|
}
|
|
|
|
const char* pMaterialname = nullptr;
|
|
|
|
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)
|
|
pMaterialname = 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 * 3 * sizeof(uint), false);
|
|
mesh.ibcount = pMesh->num_triangles * 3;
|
|
|
|
|
|
std::string mtlfilename = getFileNameWithoutExtension(filename);
|
|
mtlfilename += "_";
|
|
mtlfilename += pMaterialname;
|
|
mtlfilename += ".mtl";
|
|
mesh.m_AlbedoTexture = LoadMtl(mtlfilename.c_str());
|
|
|
|
//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)
|
|
{
|
|
Msg("Loading OBJ file %s...", filename);
|
|
|
|
std::vector<unsigned int> vertexIndices, uvIndices, normalIndices;
|
|
std::vector<glm::vec3> temp_vertices;
|
|
std::vector<glm::vec2> temp_uvs;
|
|
std::vector<glm::vec3> temp_normals;
|
|
|
|
std::vector<glm::vec3> out_vertices;
|
|
std::vector<glm::vec2> out_uvs;
|
|
std::vector<glm::vec3> out_normals;
|
|
|
|
FILE* file = fopen(filename, "r");
|
|
if (file == NULL) {
|
|
Msg("Model::LoadObj: Impossible to open the file !");
|
|
return;
|
|
}
|
|
|
|
while (1) {
|
|
|
|
char lineHeader[128];
|
|
// read the first word of the line
|
|
int res = fscanf(file, "%s", lineHeader);
|
|
if (res == EOF)
|
|
break; // EOF = End Of File. Quit the loop.
|
|
|
|
// else : parse lineHeader
|
|
|
|
if (strcmp(lineHeader, "v") == 0) {
|
|
glm::vec3 vertex;
|
|
fscanf(file, "%f %f %f\n", &vertex.x, &vertex.y, &vertex.z);
|
|
temp_vertices.push_back(vertex);
|
|
}
|
|
else if (strcmp(lineHeader, "vt") == 0) {
|
|
glm::vec2 uv;
|
|
fscanf(file, "%f %f\n", &uv.x, &uv.y);
|
|
uv.y = -uv.y; // Invert V coordinate since we will only use DDS texture, which are inverted. Remove if you want to use TGA or BMP loaders.
|
|
temp_uvs.push_back(uv);
|
|
}
|
|
else if (strcmp(lineHeader, "vn") == 0) {
|
|
glm::vec3 normal;
|
|
fscanf(file, "%f %f %f\n", &normal.x, &normal.y, &normal.z);
|
|
temp_normals.push_back(normal);
|
|
}
|
|
else if (strcmp(lineHeader, "f") == 0) {
|
|
std::string vertex1, vertex2, vertex3;
|
|
unsigned int vertexIndex[3], uvIndex[3], normalIndex[3];
|
|
int matches = fscanf(file, "%d/%d/%d %d/%d/%d %d/%d/%d\n", &vertexIndex[0], &uvIndex[0], &normalIndex[0], &vertexIndex[1], &uvIndex[1], &normalIndex[1], &vertexIndex[2], &uvIndex[2], &normalIndex[2]);
|
|
if (matches != 9) {
|
|
Msg("Model::LoadObj: File can't be read by our simple parser :-( Try exporting with other options");
|
|
fclose(file);
|
|
return;
|
|
}
|
|
vertexIndices.push_back(vertexIndex[0]);
|
|
vertexIndices.push_back(vertexIndex[1]);
|
|
vertexIndices.push_back(vertexIndex[2]);
|
|
uvIndices.push_back(uvIndex[0]);
|
|
uvIndices.push_back(uvIndex[1]);
|
|
uvIndices.push_back(uvIndex[2]);
|
|
normalIndices.push_back(normalIndex[0]);
|
|
normalIndices.push_back(normalIndex[1]);
|
|
normalIndices.push_back(normalIndex[2]);
|
|
}
|
|
else {
|
|
// Probably a comment, eat up the rest of the line
|
|
char stupidBuffer[1000];
|
|
fgets(stupidBuffer, 1000, file);
|
|
}
|
|
|
|
}
|
|
|
|
// For each vertex of each triangle
|
|
for (unsigned int i = 0; i < vertexIndices.size(); i++) {
|
|
|
|
// Get the indices of its attributes
|
|
unsigned int vertexIndex = vertexIndices[i];
|
|
unsigned int uvIndex = uvIndices[i];
|
|
unsigned int normalIndex = normalIndices[i];
|
|
|
|
// Get the attributes thanks to the index
|
|
glm::vec3 vertex = temp_vertices[vertexIndex - 1];
|
|
glm::vec2 uv = temp_uvs[uvIndex - 1];
|
|
glm::vec3 normal = temp_normals[normalIndex - 1];
|
|
|
|
// Put the attributes in buffers
|
|
out_vertices.push_back(vertex);
|
|
out_uvs.push_back(uv);
|
|
out_normals.push_back(normal);
|
|
|
|
}
|
|
|
|
fclose(file);
|
|
|
|
m_boundingBox.m_min = glm::vec3(FLT_MAX);
|
|
m_boundingBox.m_max = glm::vec3(FLT_MIN);
|
|
|
|
// Combine in to the one array
|
|
std::vector<StaticMeshVertex> vertices;
|
|
for (unsigned int i = 0; i < vertexIndices.size(); i++)
|
|
{
|
|
// Get the indices of its attributes
|
|
unsigned int vertexIndex = vertexIndices[i];
|
|
unsigned int uvIndex = uvIndices[i];
|
|
unsigned int normalIndex = normalIndices[i];
|
|
|
|
// Get the attributes thanks to the index
|
|
glm::vec3 vertex = temp_vertices[vertexIndex - 1];
|
|
glm::vec2 uv = temp_uvs[uvIndex - 1];
|
|
glm::vec3 normal = temp_normals[normalIndex - 1];
|
|
|
|
StaticMeshVertex vtx = {};
|
|
vtx.position = vertex;
|
|
vtx.normal = normal;
|
|
vtx.texcoord = uv;
|
|
vertices.push_back(vtx);
|
|
|
|
m_boundingBox.m_min = glm::min(m_boundingBox.m_min, vertex);
|
|
m_boundingBox.m_max = glm::max(m_boundingBox.m_max, vertex);
|
|
}
|
|
|
|
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";
|
|
m_data.m_AlbedoTexture = LoadMtl(mtlfilename.c_str());
|
|
}
|
|
|
|
Texture2D* Model::LoadMtl(const char* filename)
|
|
{
|
|
Msg("Loading MTL file %s...", filename);
|
|
|
|
Texture2D* pAlbedo = nullptr;
|
|
|
|
FILE* file = fopen(filename, "r");
|
|
if (file == NULL) {
|
|
Msg("Model::LoadMtl: Impossible to open the file !");
|
|
return nullptr;
|
|
}
|
|
|
|
while (1) {
|
|
|
|
char lineHeader[128];
|
|
// read the first word of the line
|
|
int res = fscanf(file, "%s", lineHeader);
|
|
if (res == EOF)
|
|
break; // EOF = End Of File. Quit the loop.
|
|
|
|
if (strcmp(lineHeader, "map_Kd") == 0) {
|
|
char stupidBuffer[1000];
|
|
fgets(stupidBuffer, 1000, file);
|
|
|
|
const char* textureFilename = stupidBuffer + 1;
|
|
pAlbedo = g_texturesManager->LoadTexture2D(textureFilename, true);
|
|
}
|
|
|
|
//if (strcmp(lineHeader, "v") == 0) {
|
|
// glm::vec3 vertex;
|
|
// fscanf(file, "%f %f %f\n", &vertex.x, &vertex.y, &vertex.z);
|
|
// temp_vertices.push_back(vertex);
|
|
//}
|
|
//else {
|
|
// // Probably a comment, eat up the rest of the line
|
|
// char stupidBuffer[1000];
|
|
// fgets(stupidBuffer, 1000, file);
|
|
//}
|
|
|
|
}
|
|
|
|
fclose(file);
|
|
|
|
return pAlbedo;
|
|
}
|
|
|
|
void Model::Draw(const glm::mat4& model, SkeletonInstance* instance /*= nullptr*/)
|
|
{
|
|
SDL_assert(g_unlitShader);
|
|
|
|
glm::vec3 pos = model[3];
|
|
|
|
Shader* shader = g_unlitSkinnedShader; //instance ? g_unlitSkinnedShader : g_unlitShader;
|
|
|
|
static glm::mat4 s_identityBones[128];
|
|
static bool ready = false;
|
|
if (!ready)
|
|
{
|
|
for (int i = 0; i < 128; i++)
|
|
s_identityBones[i] = glm::mat4(1.0f);
|
|
|
|
ready = true;
|
|
}
|
|
|
|
DLight* light = nullptr;
|
|
|
|
float lastDistSq = FLT_MAX;
|
|
|
|
for (int i = 0; i < DLightManager::GetInstance()->GetNumLights(); i++)
|
|
{
|
|
float distSq = glm::length2(pos - DLightManager::GetInstance()->GetDLight(i)->position );
|
|
|
|
if (distSq <= lastDistSq)
|
|
{
|
|
lastDistSq = distSq;
|
|
light = DLightManager::GetInstance()->GetDLight(i);
|
|
}
|
|
}
|
|
|
|
if (light)
|
|
shader = instance ? g_litSkinnedShader : g_litShader;
|
|
|
|
for (int i = 0; i < m_meshes.size(); i++)
|
|
{
|
|
ModelData_t& m_data = m_meshes[i];
|
|
|
|
glFrontFace(GL_CCW);
|
|
glDepthFunc(GL_LESS);
|
|
|
|
g_renderDevice->SetCullFace(true);
|
|
g_renderDevice->SetDepthTest(true);
|
|
g_renderDevice->SetDepthWrite(true);
|
|
|
|
g_renderDevice->SetBlending(false);
|
|
|
|
g_renderDevice->SetVerticesBuffer(m_data.vb);
|
|
if (m_data.ib)
|
|
g_renderDevice->SetIndicesBuffer(m_data.ib);
|
|
|
|
g_shaderSystem->SetShader(shader);
|
|
g_shaderSystem->SetUniformMatrix(shader, UNIFORM_MODEL_MATRIX, &model[0]);
|
|
|
|
g_shaderSystem->SetUniformMatrix(shader, UNIFORM_PROJ_MATRIX, &g_render->GetProjectionMatrix()[0]);
|
|
g_shaderSystem->SetUniformMatrix(shader, UNIFORM_VIEW_MATRIX, &g_render->GetViewMatrix()[0]);
|
|
|
|
glm::mat4 mvp = glm::identity<glm::mat4>();
|
|
mvp = g_render->GetProjectionMatrix() * g_render->GetViewMatrix() * model;
|
|
g_shaderSystem->SetUniformMatrix(shader, UNIFORM_MVP_MATRIX, &mvp[0]);
|
|
|
|
|
|
Camera* camera = g_cameraManager.GetActiveCamera();
|
|
if (camera && shader->HasUniform(UNIFORM_CAMERA_POS))
|
|
{
|
|
glm::vec4 campos = glm::vec4(camera->GetPosition(), 1.0f);
|
|
g_shaderSystem->SetUniformFloat4(shader, UNIFORM_CAMERA_POS, &campos);
|
|
}
|
|
|
|
if (shader->HasUniform(UNIFORM_SUN_DIRECTION))
|
|
{
|
|
glm::vec4 lightPos = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f);
|
|
if (light)
|
|
lightPos = glm::vec4(light->position, light->radius);
|
|
|
|
g_shaderSystem->SetUniformFloat4(shader, UNIFORM_SUN_DIRECTION, &lightPos);
|
|
}
|
|
|
|
if (shader->HasUniform(UNIFORM_SUN_COLOR))
|
|
{
|
|
glm::vec4 lightColor = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f);
|
|
if (light)
|
|
lightColor = glm::vec4(light->color, 1.0f);
|
|
|
|
g_shaderSystem->SetUniformFloat4(shader, UNIFORM_SUN_COLOR, &lightColor);
|
|
}
|
|
|
|
if (shader->HasUniform(UNIFORM_SUN_AMBIENT))
|
|
{
|
|
glm::vec4 lightColor = glm::vec4(g_sceneManager->getAmbientColor(), 1.0f);
|
|
g_shaderSystem->SetUniformFloat4(shader, UNIFORM_SUN_AMBIENT, &lightColor);
|
|
}
|
|
|
|
if (!m_data.m_AlbedoTexture)
|
|
m_data.m_AlbedoTexture = g_texturesManager->LoadTexture2D("MustBeEvilHackButDontCare");
|
|
|
|
g_texturesManager->SetTexture(0, m_data.m_AlbedoTexture);
|
|
g_shaderSystem->SetUniformSampler(shader, SAMPLER_ALBEDO, 0);
|
|
|
|
if (instance)
|
|
g_shaderSystem->SetUniformMatrix(shader, UNIFORM_BONE_MATRICES, instance->m_finalMatrices.data(), instance->m_finalMatrices.size());
|
|
else
|
|
g_shaderSystem->SetUniformMatrix(shader, UNIFORM_BONE_MATRICES, s_identityBones, 128);
|
|
|
|
if (m_data.ib)
|
|
g_renderDevice->DrawElements(PT_TRIANGLES, m_data.ibcount, 0, NULL);
|
|
else
|
|
g_renderDevice->DrawArrays(PT_TRIANGLES, 0, m_data.vbcount);
|
|
|
|
}
|
|
|
|
// debug draw
|
|
//if (instance)
|
|
//{
|
|
// 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));
|
|
// }
|
|
// }
|
|
//}
|
|
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
uint32_t Model::GetNumAnimations()
|
|
{
|
|
return (uint32_t)m_animations.size();
|
|
}
|
|
|
|
const Animation* Model::GetAnimation(AnimationId_t index)
|
|
{
|
|
if (-1 >= index || index >= GetNumAnimations())
|
|
Core::Error("Model::GetAnimation: index %d is out of bounds.", index);
|
|
|
|
return &m_animations[index];
|
|
}
|
|
|
|
int Model::GetNumPoses()
|
|
{
|
|
return m_numPoses;
|
|
}
|
|
|
|
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;
|
|
|
|
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)
|
|
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];
|
|
}
|
|
|
|
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 - 1) * 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;
|
|
float t = animationFrame - frameA;
|
|
|
|
if (!instance->m_looped && frameA >= animation.numFrames - 1)
|
|
frameA = animation.numFrames - 1;
|
|
|
|
if (instance->m_time >= totalDuration)
|
|
{
|
|
frameA = animation.numFrames - 1;
|
|
frameB = animation.numFrames - 1;
|
|
t = 0.0f;
|
|
}
|
|
|
|
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];
|
|
}
|
|
|
|
}
|
|
|
|
void ReleaseModelData(ModelData_t& data)
|
|
{
|
|
if (data.ib)
|
|
{
|
|
delete data.ib;
|
|
data.ib = nullptr;
|
|
}
|
|
|
|
if (data.vb)
|
|
{
|
|
delete data.vb;
|
|
data.vb = nullptr;
|
|
}
|
|
}
|