Added Model and related classes
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
#define NOMINMAX
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <glext.h>
|
||||
#include "Core/Util/GLError.h"
|
||||
#include "Rendering/Util/GLError.h"
|
||||
|
||||
// GLM
|
||||
#define GLM_FORCE_RADIANS
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef BaseTexture_h__
|
||||
#define BaseTexture_h__
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <cstdio>
|
||||
|
||||
#include "Core/ResourceManager.h"
|
||||
#include "Rendering/PNG.h"
|
||||
|
||||
class BaseTexture : public Resource
|
||||
{
|
||||
friend class ResourceManager;
|
||||
|
||||
public:
|
||||
unsigned int Width = 0;
|
||||
unsigned int Height = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef Model_h__
|
||||
#define Model_h__
|
||||
|
||||
#include "Rendering/RawModel.h"
|
||||
|
||||
class Model : public RawModel
|
||||
{
|
||||
friend class ResourceManager;
|
||||
|
||||
private:
|
||||
Model(std::string fileName);
|
||||
|
||||
public:
|
||||
~Model();
|
||||
|
||||
GLuint VAO;
|
||||
GLuint ElementBuffer;
|
||||
|
||||
private:
|
||||
GLuint VertexBuffer;
|
||||
GLuint DiffuseVertexColorBuffer;
|
||||
GLuint SpecularVertexColorBuffer;
|
||||
GLuint NormalBuffer;
|
||||
GLuint TangentNormalsBuffer;
|
||||
GLuint BiTangentNormalsBuffer;
|
||||
GLuint TextureCoordBuffer;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef PNG_h_
|
||||
#define PNG_h_
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <png.h>
|
||||
|
||||
#include "Image.h"
|
||||
|
||||
class PNG : public Image
|
||||
{
|
||||
public:
|
||||
PNG(std::string path);
|
||||
~PNG();
|
||||
|
||||
private:
|
||||
static void pngErrorFunction(png_structp png_ptr, png_const_charp error_msg);
|
||||
static void pngWarningFunction(png_structp png_ptr, png_const_charp warning_msg);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,77 @@
|
||||
#ifndef RawModel_h__
|
||||
#define RawModel_h__
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <cstdlib>
|
||||
#include <stack>
|
||||
|
||||
#include <assimp/Importer.hpp>
|
||||
#include <assimp/scene.h>
|
||||
#include <assimp/postprocess.h>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
#include "Core/ResourceManager.h"
|
||||
#include "Rendering/Texture.h"
|
||||
#include "Rendering/Skeleton.h"
|
||||
|
||||
class RawModel : public Resource
|
||||
{
|
||||
friend class ResourceManager;
|
||||
|
||||
protected:
|
||||
RawModel(std::string fileName);
|
||||
|
||||
public:
|
||||
~RawModel();
|
||||
|
||||
struct Vertex
|
||||
{
|
||||
glm::vec3 Position;
|
||||
glm::vec3 Normal;
|
||||
glm::vec3 Tangent;
|
||||
glm::vec3 BiTangent;
|
||||
glm::vec2 TextureCoords;
|
||||
glm::vec4 DiffuseVertexColor;
|
||||
glm::vec4 SpecularVertexColor;
|
||||
glm::vec4 BoneIndices1;
|
||||
glm::vec4 BoneIndices2;
|
||||
glm::vec4 BoneWeights1;
|
||||
glm::vec4 BoneWeights2;
|
||||
};
|
||||
|
||||
struct MaterialGroup
|
||||
{
|
||||
float Shininess;
|
||||
std::shared_ptr<dd::Texture> Texture;
|
||||
std::shared_ptr<dd::Texture> NormalMap;
|
||||
std::shared_ptr<dd::Texture> SpecularMap;
|
||||
unsigned int StartIndex;
|
||||
unsigned int EndIndex;
|
||||
};
|
||||
|
||||
std::vector<MaterialGroup> TextureGroups;
|
||||
|
||||
std::vector<Vertex> m_Vertices;
|
||||
std::vector<unsigned int> m_Indices;
|
||||
Skeleton* m_Skeleton = nullptr;
|
||||
glm::mat4 m_Matrix;
|
||||
|
||||
private:
|
||||
std::vector<glm::ivec2> BoneIndices;
|
||||
std::vector<glm::vec2> BoneWeights;
|
||||
std::vector<glm::vec3> Normals;
|
||||
std::vector<glm::vec4> DiffuseVertexColor;
|
||||
std::vector<glm::vec4> SpecularVertexColor;
|
||||
std::vector<glm::vec3> TangentNormals;
|
||||
std::vector<glm::vec3> BiTangentNormals;
|
||||
std::vector<glm::vec2> TextureCoords;
|
||||
|
||||
void CreateSkeleton(std::vector<std::tuple<std::string, glm::mat4>> &boneInfo, std::map<std::string, int> &boneNameMapping, aiNode* node, int parentID);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,95 @@
|
||||
#ifndef Skeleton_h__
|
||||
#define Skeleton_h__
|
||||
|
||||
#include <sstream>
|
||||
|
||||
//struct Bone
|
||||
//{
|
||||
// Bone(std::string name, glm::mat4 offsetMatrix)
|
||||
// : Name(name)
|
||||
// , OffsetMatrix(offsetMatrix)
|
||||
// { }
|
||||
//
|
||||
// ~Bone()
|
||||
// {
|
||||
// for (auto kv : Children) {
|
||||
// delete kv.second;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// std::string Name;
|
||||
// glm::mat4 OffsetMatrix;
|
||||
// glm::mat4 LocalMatrix;
|
||||
//
|
||||
// std::map<std::string, Bone*> Children;
|
||||
//};
|
||||
|
||||
class Skeleton
|
||||
{
|
||||
public:
|
||||
struct Bone
|
||||
{
|
||||
Bone(int id, Bone* parent, std::string name, glm::mat4 offsetMatrix)
|
||||
: ID(id)
|
||||
, Parent(parent)
|
||||
, Name(name)
|
||||
, OffsetMatrix(offsetMatrix)
|
||||
{ }
|
||||
|
||||
int ID;
|
||||
std::string Name;
|
||||
glm::mat4 OffsetMatrix;
|
||||
|
||||
Bone* Parent;
|
||||
std::vector<Bone*> Children;
|
||||
};
|
||||
|
||||
struct Animation
|
||||
{
|
||||
struct Keyframe
|
||||
{
|
||||
struct BoneProperty
|
||||
{
|
||||
int ID;
|
||||
glm::vec3 Position;
|
||||
glm::quat Rotation;
|
||||
glm::vec3 Scale = glm::vec3(1);
|
||||
};
|
||||
|
||||
int Index = 0;
|
||||
double Time = 0.0;
|
||||
std::map<int, Keyframe::BoneProperty> BoneProperties;
|
||||
};
|
||||
|
||||
std::string Name;
|
||||
double Duration;
|
||||
std::vector<Keyframe> Keyframes;
|
||||
};
|
||||
|
||||
Skeleton() { }
|
||||
~Skeleton();
|
||||
|
||||
Bone* RootBone;
|
||||
|
||||
std::map<int, Bone*> Bones;
|
||||
|
||||
// Attach a new bone to the skeleton
|
||||
// Returns: New bone index
|
||||
int CreateBone(int ID, int parentID, std::string name, glm::mat4 offsetMatrix);
|
||||
|
||||
int GetBoneID(std::string name);
|
||||
|
||||
const Animation* GetAnimation(std::string name);
|
||||
std::vector<glm::mat4> GetFrameBones(const Animation& animation, double time, bool noRootMotion = false);
|
||||
void AccumulateBoneTransforms(bool noRootMotion, const Animation::Keyframe& currentFrame, const Animation::Keyframe& nextFrame, float progress, std::map<int, glm::mat4>& boneMatrices, const Bone* bone, glm::mat4 parentMatrix);
|
||||
void PrintSkeleton();
|
||||
void PrintSkeleton(const Bone* parent, int depthCount);
|
||||
std::map<std::string, Animation> Animations;
|
||||
|
||||
private:
|
||||
std::map<std::string, Bone*> m_BonesByName;
|
||||
|
||||
int GetKeyframe(const Animation& animation, double time);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef Texture_h__
|
||||
#define Texture_h__
|
||||
|
||||
#include "Rendering/BaseTexture.h"
|
||||
|
||||
class Texture : public BaseTexture
|
||||
{
|
||||
friend class ResourceManager;
|
||||
|
||||
private:
|
||||
Texture(std::string path);
|
||||
|
||||
public:
|
||||
~Texture();
|
||||
|
||||
void Bind(GLenum textureUnit = GL_TEXTURE0);
|
||||
|
||||
GLuint m_Texture = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,64 @@
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "Rendering/Model.h"
|
||||
|
||||
Model::Model(std::string fileName)
|
||||
: RawModel(fileName)
|
||||
{
|
||||
// Generate GL buffers
|
||||
GLuint buffer;
|
||||
glGenBuffers(1, &buffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, m_Vertices.size() * sizeof(Vertex), &m_Vertices[0], GL_STATIC_DRAW);
|
||||
|
||||
glGenBuffers(1, &ElementBuffer);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ElementBuffer);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_Indices.size() * sizeof(unsigned int), &m_Indices[0], GL_STATIC_DRAW);
|
||||
|
||||
glGenVertexArrays(1, &VAO);
|
||||
glBindVertexArray(VAO);
|
||||
GLERROR("GLEW: BufferFail4");
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffer);
|
||||
std::vector<int> structSizes = { 3, 3, 3, 3, 2, 4, 4, 4, 4, 4, 4 };
|
||||
int stride = 0;
|
||||
for (int size : structSizes) {
|
||||
stride += size;
|
||||
}
|
||||
stride *= sizeof(GLfloat);
|
||||
int offset = 0;
|
||||
{
|
||||
int element = 0;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * offset)); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
}
|
||||
GLERROR("GLEW: BufferFail5");
|
||||
|
||||
glEnableVertexAttribArray(0);
|
||||
glEnableVertexAttribArray(1);
|
||||
glEnableVertexAttribArray(2);
|
||||
glEnableVertexAttribArray(3);
|
||||
glEnableVertexAttribArray(4);
|
||||
glEnableVertexAttribArray(5);
|
||||
glEnableVertexAttribArray(6);
|
||||
glEnableVertexAttribArray(7);
|
||||
glEnableVertexAttribArray(8);
|
||||
glEnableVertexAttribArray(9);
|
||||
glEnableVertexAttribArray(10);
|
||||
GLERROR("GLEW: BufferFail5");
|
||||
|
||||
//CreateBuffers();
|
||||
}
|
||||
|
||||
Model::~Model()
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "Rendering/PNG.h"
|
||||
|
||||
PNG::PNG(std::string path)
|
||||
{
|
||||
FILE* file = fopen(path.c_str(), "rb");
|
||||
if (!file) {
|
||||
LOG_ERROR("Failed to open texture file \"%s\": %s", path.c_str(), const_cast<const char*>(strerror(errno)));
|
||||
return;
|
||||
}
|
||||
|
||||
png_byte header[8];
|
||||
fread(header, 1, 8, file);
|
||||
bool isPNG = !png_sig_cmp(header, 0, 8);
|
||||
if (!isPNG) {
|
||||
LOG_ERROR("Failed to load texture file \"%s\": File isn't PNG", path.c_str());
|
||||
fclose(file);
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize libpng
|
||||
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, (png_error_ptr)&PNG::pngErrorFunction, (png_error_ptr)&PNG::pngErrorFunction);
|
||||
if (!png_ptr) {
|
||||
LOG_ERROR("libpng: Failed to initialze png_struct");
|
||||
png_destroy_read_struct(&png_ptr, nullptr, nullptr);
|
||||
fclose(file);
|
||||
return;
|
||||
}
|
||||
png_infop info_ptr = png_create_info_struct(png_ptr);
|
||||
if (!info_ptr) {
|
||||
LOG_ERROR("libpng: Failed to initialze png_info");
|
||||
png_destroy_read_struct(&png_ptr, nullptr, nullptr);
|
||||
fclose(file);
|
||||
return;
|
||||
}
|
||||
png_infop info_end_ptr = png_create_info_struct(png_ptr);
|
||||
if (!info_end_ptr) {
|
||||
LOG_ERROR("libpng: Failed to initialze second png_info");
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
|
||||
fclose(file);
|
||||
return;
|
||||
}
|
||||
png_init_io(png_ptr, file);
|
||||
|
||||
// We already read the first 8 bytes of the header
|
||||
png_set_sig_bytes(png_ptr, 8);
|
||||
// Read all the info up to the image data
|
||||
png_read_info(png_ptr, info_ptr);
|
||||
|
||||
// Get info
|
||||
int bit_depth, color_type;
|
||||
unsigned int width, height;
|
||||
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL);
|
||||
if (bit_depth != 8) {
|
||||
LOG_ERROR("libpng: Unsupported bit depth \"%i\" of image \"%s\", must be 8", bit_depth, path.c_str());
|
||||
return;
|
||||
}
|
||||
switch (color_type) {
|
||||
case PNG_COLOR_TYPE_RGB:
|
||||
case PNG_COLOR_TYPE_RGBA:
|
||||
Format = Image::ImageFormat::RGBA;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("libpng: Unsupported color format \"%i\" of image \"%s\"", color_type, path.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert RGB to RGBA, since DirectX rather treat them all the same way
|
||||
if (color_type == PNG_COLOR_TYPE_RGB) {
|
||||
LOG_DEBUG("Converting RGB to RGBA for \"%s\"", path.c_str());
|
||||
png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER);
|
||||
png_read_update_info(png_ptr, info_ptr);
|
||||
}
|
||||
|
||||
unsigned int row_bytes = png_get_rowbytes(png_ptr, info_ptr);
|
||||
this->Data = new unsigned char[height * row_bytes];
|
||||
png_bytep* row_pointers = new png_bytep[height];
|
||||
|
||||
// Point each row to the continuous data array
|
||||
for (int i = 0; i < height; ++i) {
|
||||
// Invert Y for OpenGL
|
||||
row_pointers[height - 1 - i] = this->Data + i * row_bytes;
|
||||
}
|
||||
|
||||
// Read in the data
|
||||
png_read_image(png_ptr, row_pointers);
|
||||
delete[] row_pointers;
|
||||
|
||||
this->Width = width;
|
||||
this->Height = height;
|
||||
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, &info_end_ptr);
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
PNG::~PNG()
|
||||
{
|
||||
if (this->Data != nullptr) {
|
||||
delete[] this->Data;
|
||||
this->Data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void PNG::pngErrorFunction(png_structp png_ptr, png_const_charp error_msg)
|
||||
{
|
||||
LOG_WARNING("%s", error_msg);
|
||||
}
|
||||
|
||||
void PNG::pngWarningFunction(png_structp png_ptr, png_const_charp warning_msg)
|
||||
{
|
||||
LOG_WARNING("%s", warning_msg);
|
||||
}
|
||||
@@ -0,0 +1,320 @@
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "Rendering/RawModel.h"
|
||||
|
||||
RawModel::RawModel(std::string fileName)
|
||||
{
|
||||
Assimp::Importer importer;
|
||||
const aiScene* scene = importer.ReadFile(fileName, aiProcess_CalcTangentSpace | aiProcess_Triangulate);
|
||||
|
||||
if (scene == nullptr) {
|
||||
LOG_ERROR("Failed to load model \"%s\"", fileName.c_str());
|
||||
LOG_ERROR("Assimp error: %s", importer.GetErrorString());
|
||||
return;
|
||||
}
|
||||
|
||||
auto m = scene->mRootNode->mTransformation;
|
||||
m_Matrix = glm::mat4(
|
||||
m.a1, m.a2, m.a3, m.a4,
|
||||
m.b1, m.b2, m.b3, m.b4,
|
||||
m.c1, m.c2, m.c3, m.c4,
|
||||
m.d1, m.d2, m.d3, m.d4
|
||||
);
|
||||
m_Matrix = glm::transpose(m_Matrix);
|
||||
|
||||
auto meshes = scene->mMeshes;
|
||||
|
||||
// Pre-count vertices
|
||||
int numVertices = 0;
|
||||
int numIndices = 0;
|
||||
for (int i = 0; i < scene->mNumMeshes; ++i) {
|
||||
numVertices += meshes[i]->mNumVertices;
|
||||
|
||||
// Faces
|
||||
for (int j = 0; j < meshes[i]->mNumFaces; ++j) {
|
||||
auto face = meshes[i]->mFaces[j];
|
||||
numIndices += face.mNumIndices;
|
||||
}
|
||||
}
|
||||
LOG_DEBUG("Vertex count %i", numVertices);
|
||||
LOG_DEBUG("Index count %i", numIndices);
|
||||
|
||||
LOG_DEBUG("Model has %i embedded textures", scene->mNumTextures);
|
||||
|
||||
std::vector<std::tuple<std::string, glm::mat4>> boneInfo;
|
||||
std::map<std::string, int> boneNameMapping;
|
||||
|
||||
for (int i = 0; i < scene->mNumMeshes; ++i) {
|
||||
auto mesh = meshes[i];
|
||||
auto material = scene->mMaterials[mesh->mMaterialIndex];
|
||||
unsigned int indexOffset = m_Vertices.size();
|
||||
|
||||
// Vertices, normals and texture coordinates
|
||||
for (int vertexIndex = 0; vertexIndex < mesh->mNumVertices; ++vertexIndex) {
|
||||
Vertex desc;
|
||||
|
||||
// Position
|
||||
auto position = mesh->mVertices[vertexIndex];
|
||||
desc.Position = glm::vec3(position.x, position.y, position.z);
|
||||
|
||||
// Normal
|
||||
auto normal = mesh->mNormals[vertexIndex];
|
||||
desc.Normal = glm::vec3(normal.x, normal.y, normal.z);
|
||||
|
||||
//if (mesh->HasTangentsAndBitangents()) {
|
||||
// // Tangent
|
||||
// auto tangent = mesh->mTangents[vertexIndex];
|
||||
// desc.Tangent = glm::vec3(tangent.x, tangent.y, tangent.z);
|
||||
|
||||
// // Bi-tangent
|
||||
// auto bitangent = mesh->mBitangents[vertexIndex];
|
||||
// desc.BiTangent = glm::vec3(bitangent.x, bitangent.y, bitangent.z);
|
||||
//}
|
||||
|
||||
// UV
|
||||
if (mesh->HasTextureCoords(0)) {
|
||||
auto uv = mesh->mTextureCoords[0][vertexIndex];
|
||||
desc.TextureCoords = glm::vec2(uv.x, uv.y);
|
||||
}
|
||||
|
||||
// Material diffuse color
|
||||
aiColor4D diffuse;
|
||||
material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse);
|
||||
desc.DiffuseVertexColor = glm::vec4(diffuse.r, diffuse.g, diffuse.b, diffuse.a);
|
||||
// Material specular color
|
||||
aiColor4D specular;
|
||||
material->Get(AI_MATKEY_COLOR_SPECULAR, specular);
|
||||
desc.SpecularVertexColor = glm::vec4(specular.r, specular.g, specular.b, specular.a);
|
||||
|
||||
m_Vertices.push_back(desc);
|
||||
}
|
||||
|
||||
// Faces
|
||||
for (int j = 0; j < mesh->mNumFaces; ++j) {
|
||||
auto face = mesh->mFaces[j];
|
||||
for (int k = 0; k < face.mNumIndices; ++k) {
|
||||
unsigned int index = face.mIndices[k];
|
||||
m_Indices.push_back(indexOffset + index);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate normal mapping tangents
|
||||
for (int i = 0; i < m_Indices.size(); i += 3) {
|
||||
Vertex& v0 = m_Vertices[m_Indices[i]];
|
||||
Vertex& v1 = m_Vertices[m_Indices[i + 1]];
|
||||
Vertex& v2 = m_Vertices[m_Indices[i + 2]];
|
||||
|
||||
glm::vec3 edge1 = v1.Position - v0.Position;
|
||||
glm::vec3 edge2 = v2.Position - v0.Position;
|
||||
|
||||
float deltaU1 = v1.TextureCoords.x - v0.TextureCoords.x;
|
||||
float deltaV1 = v1.TextureCoords.y - v0.TextureCoords.y;
|
||||
float deltaU2 = v2.TextureCoords.x - v0.TextureCoords.x;
|
||||
float deltaV2 = v2.TextureCoords.y - v0.TextureCoords.y;
|
||||
|
||||
float f = 1.0f / (deltaU1 * deltaV2 - deltaU2 * deltaV1);
|
||||
|
||||
glm::vec3 tangent;
|
||||
tangent.x = f * (deltaV2 * edge1.x - deltaV1 * edge2.x);
|
||||
tangent.y = f * (deltaV2 * edge1.y - deltaV1 * edge2.y);
|
||||
tangent.z = f * (deltaV2 * edge1.z - deltaV1 * edge2.z);
|
||||
|
||||
v0.Tangent += tangent;
|
||||
v1.Tangent += tangent;
|
||||
v2.Tangent += tangent;
|
||||
}
|
||||
for (auto& vertex : m_Vertices) {
|
||||
vertex.Tangent = glm::normalize(vertex.Tangent);
|
||||
vertex.BiTangent = glm::normalize(glm::cross(vertex.Tangent, glm::normalize(vertex.Normal)));
|
||||
}
|
||||
|
||||
// Material info
|
||||
MaterialGroup matGroup;
|
||||
matGroup.StartIndex = indexOffset;
|
||||
matGroup.EndIndex = m_Indices.size() - 1;
|
||||
// Material shininess
|
||||
material->Get(AI_MATKEY_SHININESS, matGroup.Shininess);
|
||||
LOG_DEBUG("Shininess: %f", matGroup.Shininess);
|
||||
// Diffuse texture
|
||||
LOG_DEBUG("%i diffuse textures found", material->GetTextureCount(aiTextureType_DIFFUSE));
|
||||
if (material->GetTextureCount(aiTextureType_DIFFUSE)) {
|
||||
aiString path;
|
||||
aiTextureMapping mapping;
|
||||
material->GetTexture(aiTextureType_DIFFUSE, 0, &path, &mapping);
|
||||
std::string absolutePath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string();
|
||||
LOG_DEBUG("Diffuse texture: %s", absolutePath.c_str());
|
||||
matGroup.Texture = std::shared_ptr<Texture>(ResourceManager::Load<Texture>(absolutePath));
|
||||
}
|
||||
// Normal map
|
||||
LOG_DEBUG("%i normal maps found", material->GetTextureCount(aiTextureType_HEIGHT));
|
||||
if (material->GetTextureCount(aiTextureType_HEIGHT)) {
|
||||
aiString path;
|
||||
aiTextureMapping mapping;
|
||||
material->GetTexture(aiTextureType_HEIGHT, 0, &path, &mapping);
|
||||
std::string absolutePath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string();
|
||||
LOG_DEBUG("Normal map: %s", absolutePath.c_str());
|
||||
matGroup.NormalMap = std::shared_ptr<Texture>(ResourceManager::Load<Texture>(absolutePath));
|
||||
}
|
||||
// Specular map
|
||||
LOG_DEBUG("%i specular maps found", material->GetTextureCount(aiTextureType_SPECULAR));
|
||||
if (material->GetTextureCount(aiTextureType_SPECULAR)) {
|
||||
aiString path;
|
||||
aiTextureMapping mapping;
|
||||
material->GetTexture(aiTextureType_SPECULAR, 0, &path, &mapping);
|
||||
std::string absolutePath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string();
|
||||
LOG_DEBUG("Specular map: %s", absolutePath.c_str());
|
||||
matGroup.SpecularMap = std::shared_ptr<Texture>(ResourceManager::Load<Texture>(absolutePath));
|
||||
}
|
||||
TextureGroups.push_back(matGroup);
|
||||
|
||||
// Bones
|
||||
std::map<int, std::vector<std::tuple<int, float>>> vertexWeights;
|
||||
for (int j = 0; j < mesh->mNumBones; ++j) {
|
||||
auto bone = mesh->mBones[j];
|
||||
std::string boneName = bone->mName.C_Str();
|
||||
|
||||
auto mat = bone->mOffsetMatrix;
|
||||
glm::mat4 glmMat(mat.a1, mat.b1, mat.c1, mat.d1,
|
||||
mat.a2, mat.b2, mat.c2, mat.d2,
|
||||
mat.a3, mat.b3, mat.c3, mat.d3,
|
||||
mat.a4, mat.b4, mat.c4, mat.d4);
|
||||
|
||||
int boneIndex;
|
||||
if (boneNameMapping.find(boneName) != boneNameMapping.end()) {
|
||||
boneIndex = boneNameMapping[boneName];
|
||||
} else {
|
||||
boneIndex = boneInfo.size();
|
||||
boneInfo.push_back(std::make_tuple(boneName, glmMat));
|
||||
boneNameMapping[boneName] = boneIndex;
|
||||
}
|
||||
|
||||
for (int k = 0; k < bone->mNumWeights; ++k) {
|
||||
auto weight = bone->mWeights[k];
|
||||
unsigned int offsetVertexId = weight.mVertexId + indexOffset;
|
||||
vertexWeights[offsetVertexId].push_back(std::make_tuple(boneIndex, weight.mWeight));
|
||||
}
|
||||
}
|
||||
for (auto &pair : vertexWeights) {
|
||||
auto weights = pair.second;
|
||||
Vertex& desc = m_Vertices[pair.first];
|
||||
|
||||
const int maxWeights = 8;
|
||||
if (weights.size() > maxWeights) {
|
||||
LOG_WARNING("Vertex weights (%i) greater than max weights per vertex (%i)", weights.size(), maxWeights);
|
||||
}
|
||||
for (int weightIndex = 0; weightIndex < weights.size() && weightIndex < maxWeights && weightIndex < 4; ++weightIndex) {
|
||||
std::tie(desc.BoneIndices1[weightIndex], desc.BoneWeights1[weightIndex]) = weights[weightIndex];
|
||||
}
|
||||
for (int weightIndex = 4; weightIndex < weights.size() && weightIndex < maxWeights && weightIndex < 8; ++weightIndex) {
|
||||
std::tie(desc.BoneIndices2[weightIndex - 4], desc.BoneWeights2[weightIndex - 4]) = weights[weightIndex];
|
||||
}
|
||||
}
|
||||
|
||||
//break;
|
||||
}
|
||||
|
||||
// Traverse the node tree and build a skeleton
|
||||
if (!boneInfo.empty()) {
|
||||
m_Skeleton = new Skeleton();
|
||||
CreateSkeleton(boneInfo, boneNameMapping, scene->mRootNode, -1);
|
||||
int numBones = m_Skeleton->Bones.size();
|
||||
LOG_DEBUG("Bone count: %i", numBones);
|
||||
if (numBones > 0) {
|
||||
m_Skeleton->PrintSkeleton();
|
||||
}
|
||||
}
|
||||
|
||||
// Animations
|
||||
LOG_DEBUG("Animation count: %i", scene->mNumAnimations);
|
||||
for (int i = 0; i < scene->mNumAnimations; ++i) {
|
||||
auto animation = scene->mAnimations[i];
|
||||
std::string animationName = animation->mName.C_Str();
|
||||
LOG_DEBUG("Animation: %s", animationName.c_str());
|
||||
LOG_DEBUG("Duration: %f", animation->mDuration);
|
||||
LOG_DEBUG("Ticks per second: %f", animation->mTicksPerSecond);
|
||||
|
||||
Skeleton::Animation skelAnim;
|
||||
skelAnim.Name = animationName;
|
||||
skelAnim.Duration = animation->mDuration / animation->mTicksPerSecond;
|
||||
|
||||
std::map<int, double> frameTimes;
|
||||
std::map<int, std::map<int, Skeleton::Animation::Keyframe::BoneProperty>> frameBoneProperties;
|
||||
// For each animation channel (bone)
|
||||
for (int channelIndex = 0; channelIndex < animation->mNumChannels; ++channelIndex) {
|
||||
auto channel = animation->mChannels[channelIndex];
|
||||
std::string boneName = channel->mNodeName.C_Str();
|
||||
int boneID = m_Skeleton->GetBoneID(boneName);
|
||||
if (boneID == -1) {
|
||||
LOG_ERROR("Animation referenced a bone that doesn't exist: %s", boneName.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// If you don't have the same amount of keyframes for every transformation type you're dumb.
|
||||
if (channel->mNumPositionKeys != channel->mNumRotationKeys || channel->mNumPositionKeys != channel->mNumScalingKeys) {
|
||||
LOG_ERROR("Hey, animation! You're dumb!", animationName.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int keyframe = 0; keyframe < channel->mNumPositionKeys; ++keyframe) {
|
||||
auto posKey = channel->mPositionKeys[keyframe];
|
||||
auto rotKey = channel->mRotationKeys[keyframe];
|
||||
auto scaleKey = channel->mScalingKeys[keyframe];
|
||||
|
||||
frameTimes[keyframe] = posKey.mTime;
|
||||
|
||||
auto &property = frameBoneProperties[keyframe][boneID];
|
||||
property.ID = keyframe;
|
||||
property.Position = glm::vec3(posKey.mValue.x, posKey.mValue.y, posKey.mValue.z);
|
||||
property.Rotation = glm::quat(rotKey.mValue.w, rotKey.mValue.x, rotKey.mValue.y, rotKey.mValue.z);
|
||||
property.Scale = glm::vec3(scaleKey.mValue.x, scaleKey.mValue.y, scaleKey.mValue.z);
|
||||
}
|
||||
}
|
||||
|
||||
// Create keyframes from bone properties
|
||||
for (auto &kv : frameBoneProperties) {
|
||||
int keyframe = kv.first;
|
||||
Skeleton::Animation::Keyframe animationFrame;
|
||||
animationFrame.Index = keyframe;
|
||||
animationFrame.Time = frameTimes[keyframe] / animation->mTicksPerSecond;
|
||||
// HACK: For some reason Blender likes to create a first frame that doesn't start at time 0
|
||||
if (keyframe == 0) {
|
||||
animationFrame.Time = 0;
|
||||
}
|
||||
for (auto &kv2 : kv.second) {
|
||||
int boneID = kv2.first;
|
||||
auto &property = kv2.second;
|
||||
animationFrame.BoneProperties[boneID] = property;
|
||||
}
|
||||
skelAnim.Keyframes.push_back(animationFrame);
|
||||
}
|
||||
|
||||
m_Skeleton->Animations[animationName] = skelAnim;
|
||||
}
|
||||
}
|
||||
|
||||
RawModel::~RawModel()
|
||||
{
|
||||
if (m_Skeleton) {
|
||||
delete m_Skeleton;
|
||||
}
|
||||
}
|
||||
|
||||
void RawModel::CreateSkeleton(std::vector<std::tuple<std::string, glm::mat4>> &boneInfo, std::map<std::string, int> &boneNameMapping, aiNode* node, int parentID)
|
||||
{
|
||||
std::string nodeName = node->mName.C_Str();
|
||||
|
||||
// Find the bone by name in the bone info list
|
||||
if (boneNameMapping.find(nodeName) == boneNameMapping.end()) {
|
||||
LOG_DEBUG("Node \"%s\" was not a bone", nodeName.c_str());
|
||||
} else {
|
||||
glm::mat4 offsetMatrix;
|
||||
int ID = boneNameMapping[nodeName];
|
||||
std::tie(std::ignore, offsetMatrix) = boneInfo[ID];
|
||||
m_Skeleton->CreateBone(ID, parentID, nodeName, offsetMatrix);
|
||||
parentID = ID;
|
||||
}
|
||||
|
||||
for (int childIndex = 0; childIndex < node->mNumChildren; ++childIndex) {
|
||||
aiNode* child = node->mChildren[childIndex];
|
||||
CreateSkeleton(boneInfo, boneNameMapping, child, parentID);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "Rendering/Skeleton.h"
|
||||
|
||||
int Skeleton::CreateBone(int ID, int parentID, std::string name, glm::mat4 offsetMatrix)
|
||||
{
|
||||
if (m_BonesByName.find(name) != m_BonesByName.end()) {
|
||||
return m_BonesByName.at(name)->ID;
|
||||
} else {
|
||||
Bone* bone;
|
||||
|
||||
if (parentID == -1) {
|
||||
bone = new Bone(ID, nullptr, name, offsetMatrix);
|
||||
RootBone = bone;
|
||||
} else {
|
||||
Bone* parent = Bones[parentID];
|
||||
bone = new Bone(ID, parent, name, offsetMatrix);
|
||||
parent->Children.push_back(bone);
|
||||
}
|
||||
|
||||
Bones[ID] = bone;
|
||||
m_BonesByName[name] = bone;
|
||||
return ID;
|
||||
}
|
||||
}
|
||||
|
||||
Skeleton::~Skeleton()
|
||||
{
|
||||
for (auto &kv : Bones) {
|
||||
delete kv.second;
|
||||
}
|
||||
}
|
||||
|
||||
const Skeleton::Animation* Skeleton::GetAnimation(std::string name)
|
||||
{
|
||||
auto it = Animations.find(name);
|
||||
if (it != Animations.end()) {
|
||||
return const_cast<const Animation*>(&it->second);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<glm::mat4> Skeleton::GetFrameBones(const Animation& animation, double time, bool noRootMotion /*= false*/)
|
||||
{
|
||||
// HACK: Animation wrap-around
|
||||
while (time < 0) {
|
||||
time += animation.Duration;
|
||||
}
|
||||
while (time > animation.Duration) {
|
||||
time -= animation.Duration;
|
||||
}
|
||||
|
||||
int currentKeyframeIndex = GetKeyframe(animation, time);
|
||||
|
||||
const Animation::Keyframe& currentFrame = animation.Keyframes[currentKeyframeIndex];
|
||||
const Animation::Keyframe& nextFrame = animation.Keyframes[(currentKeyframeIndex + 1) % animation.Keyframes.size()];
|
||||
float alpha = (time - currentFrame.Time) / (nextFrame.Time - currentFrame.Time);
|
||||
|
||||
//auto animationFrame = Animations[""].Keyframes[frame];
|
||||
std::map<int, glm::mat4> frameBones;
|
||||
AccumulateBoneTransforms(noRootMotion, currentFrame, nextFrame, alpha, frameBones, RootBone, glm::mat4(1));
|
||||
|
||||
std::vector<glm::mat4> finalMatrices;
|
||||
for (auto &kv : frameBones) {
|
||||
finalMatrices.push_back(kv.second);
|
||||
}
|
||||
return finalMatrices;
|
||||
}
|
||||
|
||||
void Skeleton::AccumulateBoneTransforms(bool noRootMotion, const Animation::Keyframe ¤tFrame, const Animation::Keyframe &nextFrame, float progress, std::map<int, glm::mat4> &boneMatrices, const Bone* bone, glm::mat4 parentMatrix)
|
||||
{
|
||||
glm::mat4 boneMatrix;
|
||||
|
||||
if (currentFrame.BoneProperties.find(bone->ID) != currentFrame.BoneProperties.end() || nextFrame.BoneProperties.find(bone->ID) != nextFrame.BoneProperties.end()) {
|
||||
Animation::Keyframe::BoneProperty currentBoneProperty = currentFrame.BoneProperties.at(bone->ID);
|
||||
Animation::Keyframe::BoneProperty nextBoneProperty = nextFrame.BoneProperties.at(bone->ID);
|
||||
|
||||
glm::vec3 positionInterp = currentBoneProperty.Position * (1.f - progress) + nextBoneProperty.Position * progress;
|
||||
glm::quat rotationInterp = glm::slerp(currentBoneProperty.Rotation, nextBoneProperty.Rotation, progress);
|
||||
glm::vec3 scaleInterp = currentBoneProperty.Scale * (1.f - progress) + nextBoneProperty.Scale * progress;
|
||||
|
||||
// Flag for no root motion
|
||||
if (bone == RootBone && noRootMotion) {
|
||||
positionInterp.x = 0;
|
||||
positionInterp.z = 0;
|
||||
}
|
||||
|
||||
boneMatrix = parentMatrix * (glm::translate(positionInterp) * glm::toMat4(rotationInterp) * glm::scale(scaleInterp));
|
||||
boneMatrices[bone->ID] = boneMatrix * bone->OffsetMatrix;
|
||||
} else {
|
||||
boneMatrix = parentMatrix * bone->Parent->OffsetMatrix; // * glm::inverse(bone->OffsetMatrix);
|
||||
boneMatrices[bone->ID] = boneMatrix; // * bone->OffsetMatrix;
|
||||
}
|
||||
|
||||
for (auto &child : bone->Children) {
|
||||
std::string name = child->Name;
|
||||
AccumulateBoneTransforms(noRootMotion, currentFrame, nextFrame, progress, boneMatrices, child, boneMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
int Skeleton::GetBoneID(std::string name)
|
||||
{
|
||||
if (m_BonesByName.find(name) == m_BonesByName.end()) {
|
||||
return -1;
|
||||
} else {
|
||||
return m_BonesByName.at(name)->ID;
|
||||
}
|
||||
}
|
||||
|
||||
void Skeleton::PrintSkeleton()
|
||||
{
|
||||
if (LOG_LEVEL < LOG_LEVEL_DEBUG) {
|
||||
return;
|
||||
}
|
||||
PrintSkeleton(RootBone, 0);
|
||||
}
|
||||
|
||||
void Skeleton::PrintSkeleton(const Bone* bone, int depthCount)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::string(depthCount, ' ');
|
||||
ss << bone->ID << ": " << bone->Name;
|
||||
std::cout << ss.str() << std::endl;
|
||||
|
||||
depthCount++;
|
||||
|
||||
for (auto &child : bone->Children) {
|
||||
PrintSkeleton(child, depthCount);
|
||||
}
|
||||
}
|
||||
|
||||
int Skeleton::GetKeyframe(const Animation& animation, double time)
|
||||
{
|
||||
if (time < 0) {
|
||||
time = 0;
|
||||
}
|
||||
if (time >= animation.Duration) {
|
||||
return animation.Keyframes.size() - 1;
|
||||
}
|
||||
|
||||
for (int keyframe = 0; keyframe < animation.Keyframes.size(); ++keyframe) {
|
||||
if (animation.Keyframes[keyframe].Time > time) {
|
||||
return (keyframe - 1) % animation.Keyframes.size();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "Rendering/Texture.h"
|
||||
|
||||
Texture::Texture(std::string path)
|
||||
{
|
||||
PNG image(path);
|
||||
|
||||
if (image.Width == 0 && image.Height == 0 || image.Format == Image::ImageFormat::Unknown) {
|
||||
image = PNG("Textures/Core/ErrorTexture.png");
|
||||
if (image.Width == 0 && image.Height == 0 || image.Format == Image::ImageFormat::Unknown) {
|
||||
LOG_ERROR("Couldn't even load the error texture. This is a dark day indeed.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this->Width = image.Width;
|
||||
this->Height = image.Height;
|
||||
|
||||
GLint format;
|
||||
switch (image.Format) {
|
||||
case Image::ImageFormat::RGB:
|
||||
format = GL_RGB;
|
||||
break;
|
||||
case Image::ImageFormat::RGBA:
|
||||
format = GL_RGBA;
|
||||
break;
|
||||
}
|
||||
|
||||
// Construct the OpenGL texture
|
||||
glGenTextures(1, &m_Texture);
|
||||
glBindTexture(GL_TEXTURE_2D, m_Texture);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, format, image.Width, image.Height, 0, format, GL_UNSIGNED_BYTE, image.Data);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
GLERROR("Texture load");
|
||||
}
|
||||
|
||||
Texture::~Texture()
|
||||
{
|
||||
glDeleteTextures(1, &m_Texture);
|
||||
}
|
||||
|
||||
void Texture::Bind(GLenum textureUnit /* = GL_TEXTURE0 */)
|
||||
{
|
||||
glActiveTexture(textureUnit);
|
||||
glBindTexture(GL_TEXTURE_2D, m_Texture);
|
||||
}
|
||||
Reference in New Issue
Block a user