#include #include #include #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 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 n) { return endianswap16(n); } template<> inline short endianswap(short n) { return endianswap16(n); } template<> inline uint endianswap(uint n) { return endianswap32(n); } template<> inline int endianswap(int n) { return endianswap32(n); } template inline void endianswap(T* buf, int len) { for (T* end = &buf[len]; buf < end; buf++) *buf = endianswap(*buf); } template inline T lilswap(T n) { return islittleendian() ? n : endianswap(n); } template inline void lilswap(T* buf, int len) { if (!islittleendian()) endianswap(buf, len); } template inline T bigswap(T n) { return islittleendian() ? endianswap(n) : n; } template inline void bigswap(T* buf, int len) { if (islittleendian()) endianswap(buf, len); } template T getval(FILE* f) { T n; return fread(&n, 1, sizeof(n), f) == sizeof(n) ? n : 0; } template T getlil(FILE* f) { return lilswap(getval(f)); } template T getbig(FILE* f) { return bigswap(getval(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 vertexIndices, uvIndices, normalIndices; std::vector temp_vertices; std::vector temp_uvs; std::vector temp_normals; std::vector out_vertices; std::vector out_uvs; std::vector 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 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(); 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; } }