Bubba-3D  0.9.0
Awesome game engine!
Mesh.cpp
1 /*
2  * This file is part of Bubba-3D.
3  *
4  * Bubba-3D is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * Bubba-3D is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with Bubba-3D. If not, see http://www.gnu.org/licenses/.
16  */
17 #include "Mesh.h"
18 #include <ResourceManager.h>
19 #include "Logger.h"
20 #include "Triangle.h"
21 
22 #include <assimp/Importer.hpp>
23 #include <assimp/postprocess.h>
24 #include <assimp/scene.h>
25 #include <vector>
26 #include "Chunk.h"
27 #include <string>
28 #include "Utils.h"
29 #include "GL/glew.h"
30 #include "BoneMatrices.h"
31 #include "Utils.h"
32 #include "linmath/float3x3.h"
33 #include "BoneTransformer.h"
34 
35 using namespace chag;
36 
37 #define BONE_ID_LOCATION_GPU 6
38 #define BONE_WEIGHT_LOCATION_GPU 7
39 
40 Mesh::Mesh() {
41  numAnimations = 0;
42 }
43 
44 void Mesh::loadMesh(const std::string &fileName) {
45  Logger::logInfo("Loading mesh " + fileName);
46 
47  importer.ReadFile(
48  fileName.c_str(), aiProcess_GenSmoothNormals | aiProcess_Triangulate | aiProcess_CalcTangentSpace);
49  aiScene* aiScene = importer.GetOrphanedScene();
50 
51  if (!aiScene) {
52  Logger::logError("Error loading mesh for " + fileName + ". Error message: " + importer.GetErrorString());
53  } else {
54  boneTransformer = std::make_shared<BoneTransformer>(BoneTransformer(aiScene));
55  initMesh(aiScene, fileName);
56  }
57 }
58 
59 void Mesh::initMesh(const aiScene *assimpScene, const std::string &fileNameOfMesh) {
60  for (unsigned int i = 0; i < assimpScene->mNumMeshes; i++) {
61  const aiMesh *paiMesh = assimpScene->mMeshes[i];
62  initMeshFromAiMesh(i, paiMesh);
63  }
64 
65  initMaterials(assimpScene, fileNameOfMesh);
66  createTriangles();
67 
68  numAnimations = assimpScene->mNumAnimations;
69 }
70 
71 
72 void Mesh::initMeshFromAiMesh(unsigned int index, const aiMesh *paiMesh) {
73  Chunk chunk;
74 
75  initChunkFromAiMesh(paiMesh, chunk);
76  setupSphere(&chunk.m_positions);
77 }
78 
79 void Mesh::initChunkFromAiMesh(const aiMesh *paiMesh, Chunk &chunk) {
80  initVerticesFromAiMesh(paiMesh, chunk);
81  initIndicesFromAiMesh(paiMesh, chunk);
82 
83  initBonesFromAiMesh(paiMesh, chunk);
84 
85 
86  chunk.materialIndex = paiMesh->mMaterialIndex;
87 
88  setupChunkForRendering(chunk);
89  m_chunks.push_back(chunk);
90 }
91 
92 
93 void Mesh::assertAllVertexWeightsSumToOne(Chunk &chunk) {
94  for(unsigned int i = 0; i < chunk.bones.size(); i++) {
95  BoneInfluenceOnVertex boneInfluenceOnVertex = chunk.bones[i];
96  float sum = 0.0f;
97  for (int boneIndex = 0; boneIndex < MAX_NUM_BONES; boneIndex++) {
98  sum += boneInfluenceOnVertex.weights[boneIndex];
99  }
100 
101  assert(fequals(sum, 1.0f));
102  }
103 }
104 
105 
106 void Mesh::initVerticesFromAiMesh(const aiMesh *paiMesh, Chunk &chunk) {
107  const aiVector3D Zero3D(0.0f, 0.0f, 0.0f);
108 
109  for (unsigned int i = 0; i < paiMesh->mNumVertices; i++) {
110  const aiVector3D pPos = paiMesh->mVertices[i];
111  const aiVector3D pNormal = paiMesh->mNormals[i];
112  const aiVector3D pTexCoord = paiMesh->HasTextureCoords(0) ? paiMesh->mTextureCoords[0][i] : Zero3D;
113 
114  chunk.m_positions.push_back(make_vector(pPos.x, pPos.y, pPos.z));
115  chunk.m_normals.push_back(make_vector(pNormal.x, pNormal.y, pNormal.z));
116  chunk.m_uvs.push_back(make_vector(pTexCoord.x, pTexCoord.y));
117 
118  if (paiMesh->HasTangentsAndBitangents()) {
119  const aiVector3D pBitTangents = paiMesh->mBitangents[i];
120  const aiVector3D pTangents = paiMesh->mTangents[i];
121  chunk.m_bittangents.push_back(make_vector(pBitTangents.x, pBitTangents.y, pBitTangents.z));
122  chunk.m_tangents.push_back(make_vector(pTangents.x, pTangents.y, pTangents.z));
123  }
124 
125  updateMinAndMax(pPos.x, pPos.y, pPos.z, &m_aabb.minV, &m_aabb.maxV);
126  }
127 }
128 
129 void Mesh::initIndicesFromAiMesh(const aiMesh *paiMesh, Chunk &chunk) {
130  for (unsigned int i = 0; i < paiMesh->mNumFaces; i++) {
131  const aiFace face = paiMesh->mFaces[i];
132  chunk.m_indices.push_back(face.mIndices[0]);
133  chunk.m_indices.push_back(face.mIndices[1]);
134  chunk.m_indices.push_back(face.mIndices[2]);
135  }
136 }
137 
138 void Mesh::initBonesFromAiMesh(const aiMesh *paiMesh, Chunk &chunk) {
139  if(paiMesh->mNumBones == 0) {
140  return;
141  }
142 
143  chunk.bones.resize(paiMesh->mNumVertices);
144 
145  int boneIndex = 0;
146  for(unsigned int i = 0; i < paiMesh->mNumBones; i++) {
147 
148  boneIndex = boneTransformer->createBoneIndexIfAbsent(paiMesh->mBones[i]);
149 
150  for (unsigned int j = 0; j < paiMesh->mBones[i]->mNumWeights; j++) {
151  auto aiWeight = paiMesh->mBones[i]->mWeights[j];
152  uint vertexId = aiWeight.mVertexId;
153  float weight = aiWeight.mWeight;
154 
155  chunk.bones[vertexId].addBoneData(boneIndex, weight);
156  }
157  }
158 
159  assertAllVertexWeightsSumToOne(chunk);
160 }
161 
162 void Mesh::initMaterials(const aiScene *pScene, const std::string &fileNameOfMesh) {
163  for (unsigned int i = 0; i < pScene->mNumMaterials; i++) {
164  const aiMaterial *material = pScene->mMaterials[i];
165  Material m;
166 
167  initMaterialTextures(&m, fileNameOfMesh, material);
168  initMaterialColors(&m, material);
169  initMaterialShininess(&m, material);
170 
171  materials.push_back(m);
172  }
173 }
174 
175 void Mesh::initMaterialTextures(Material *material, std::string fileNameOfMesh, const aiMaterial *loadedMaterial) {
176  if (loadedMaterial->GetTextureCount(aiTextureType_DIFFUSE) > 0) {
177  material->diffuseTexture = getTexture(loadedMaterial, fileNameOfMesh, aiTextureType_DIFFUSE);
178  }
179  if (loadedMaterial->GetTextureCount(aiTextureType_HEIGHT) > 0) {
180  material->bumpMapTexture = getTexture(loadedMaterial, fileNameOfMesh, aiTextureType_HEIGHT);
181  }
182 }
183 
184 void Mesh::initMaterialColors(Material *material, const aiMaterial *loadedMaterial) {
185  material->ambientColor = getColorFromMaterial(AI_MATKEY_COLOR_AMBIENT, *loadedMaterial);
186  material->diffuseColor = getColorFromMaterial(AI_MATKEY_COLOR_DIFFUSE, *loadedMaterial);
187  material->specularColor = getColorFromMaterial(AI_MATKEY_COLOR_SPECULAR, *loadedMaterial);
188  material->emissiveColor = getColorFromMaterial(AI_MATKEY_COLOR_EMISSIVE, *loadedMaterial);
189 }
190 
191 void Mesh::initMaterialShininess(Material *material, const aiMaterial *loadedMaterial) {
192  float specExp;
193  loadedMaterial->Get(AI_MATKEY_SHININESS, specExp);
194  material->specularExponent = specExp > 0.0f ? specExp : 0.0f;
195 }
196 
197 float3 Mesh::getColorFromMaterial(const char* colorTypeString, unsigned int type, unsigned int index, const aiMaterial &material) {
198  aiColor3D color;
199  material.Get(colorTypeString, type, index, color);
200  return make_vector(color.r, color.g, color.b);
201 }
202 
203 Texture *Mesh::getTexture(const aiMaterial *material, const std::string &fileNameOfMesh, aiTextureType type) {
204  aiString texturePath;
205  if (material->GetTexture(type, 0, &texturePath, NULL, NULL, NULL, NULL, NULL) != AI_SUCCESS) {
206  return NULL;
207  }
208 
209  std::string absolutePath = getPathOfTexture(fileNameOfMesh, std::string(texturePath.data));
210  return ResourceManager::loadAndFetchTexture(absolutePath);
211 }
212 
213 std::string Mesh::getPathOfTexture(const std::string &fileName, std::string textureName) {
214  std::string dir = getDirectoryFromPath(fileName);
215  std::string filePath = cleanFileName(textureName);
216  return dir + "/" + filePath;
217 }
218 
219 std::string Mesh::getDirectoryFromPath(const std::string &fileName) {
220  std::string::size_type index = fileName.find_last_of("/");
221  if (index == std::string::npos) {
222  return ".";
223  } else if (index == 0) {
224  return "/";
225  } else {
226  return fileName.substr(0, index);
227  }
228 }
229 
230 std::string Mesh::cleanFileName(std::string fileName) {
231  if (fileName.substr(0, 2) == ".\\") {
232  return fileName.substr(2, fileName.size() - 2);
233  } else {
234  return fileName;
235  }
236 }
237 
239  return sphere;
240 }
241 
242 void Mesh::setupSphere(std::vector<float3> *positions) {
243  sphere.setPosition(m_aabb.getCenterPosition());
244  sphere.setRadius(0.0f);
245  for (float3 posIt : *positions) {
246  float rad = length(sphere.getPosition() - posIt);
247  if (rad > sphere.getRadius()) {
248  sphere.setRadius(rad);
249  }
250  }
251 }
252 
253 
254 // TODO(Bubbad) Remove all GL dependencies directly in mesh
255 template <typename T>
256 void setupGlBuffer(std::vector<T> buffer, GLuint *bufferGLObject, GLuint vertexAttibute, int numbersPerObject,
257  const void* firstObject, GLenum bufferType, GLuint dataType) {
258  glGenBuffers(1, bufferGLObject);
259  glBindBuffer(bufferType, *bufferGLObject);
260  glBufferData(bufferType, buffer.size() * sizeof(buffer[0]), firstObject, GL_STATIC_DRAW);
261  glVertexAttribPointer(vertexAttibute, numbersPerObject, dataType, GL_FALSE, 0, 0);
262  glEnableVertexAttribArray(vertexAttibute);
263 }
264 
265 void Mesh::setupChunkForRendering(Chunk &chunk) {
266  glGenVertexArrays(1, &chunk.m_vaob);
267  glBindVertexArray(chunk.m_vaob);
268 
269  setupGlBuffer(chunk.m_positions, &chunk.m_positions_bo, 0, 3 , &chunk.m_positions[0].x, GL_ARRAY_BUFFER_ARB, GL_FLOAT);
270  setupGlBuffer(chunk.m_normals, &chunk.m_normals_bo, 1, 3, &chunk.m_normals[0].x, GL_ARRAY_BUFFER_ARB, GL_FLOAT);
271 
272  if (chunk.m_uvs.size() > 0) {
273  setupGlBuffer(chunk.m_uvs, &chunk.m_uvs_bo, 2, 2, &chunk.m_uvs[0].x, GL_ARRAY_BUFFER_ARB, GL_FLOAT);
274  }
275 
276  setupGlBuffer(chunk.m_indices, &chunk.m_ind_bo, 3, 3, &chunk.m_indices[0], GL_ELEMENT_ARRAY_BUFFER_ARB, GL_FLOAT);
277 
278  if (chunk.m_bittangents.size() > 0) {
279  setupGlBuffer(chunk.m_tangents, &chunk.m_tangents_bo, 4, 3, &chunk.m_tangents[0].x, GL_ARRAY_BUFFER_ARB, GL_FLOAT);
280  setupGlBuffer(chunk.m_bittangents, &chunk.m_bittangents_bo, 5, 3,
281  &chunk.m_bittangents[0].x, GL_ARRAY_BUFFER_ARB, GL_FLOAT);
282  }
283 
284  if(chunk.bones.size() > 0 ) {
285  glGenBuffers(1, &chunk.bonesBufferObject);
286  glBindBuffer(GL_ARRAY_BUFFER, chunk.bonesBufferObject);
287  glBufferData(GL_ARRAY_BUFFER, chunk.bones.size() * sizeof(chunk.bones[0]), &chunk.bones[0], GL_STATIC_DRAW);
288 
289  const int boneIdsPerObject = 4;
290  glVertexAttribIPointer(BONE_ID_LOCATION_GPU, boneIdsPerObject, GL_INT, sizeof(BoneInfluenceOnVertex), 0);
291  glEnableVertexAttribArray(BONE_ID_LOCATION_GPU);
292 
293  const int boneWeightsPerObject = 4;
294  glVertexAttribPointer(BONE_WEIGHT_LOCATION_GPU, boneWeightsPerObject, GL_FLOAT, GL_FALSE, sizeof(BoneInfluenceOnVertex), (const GLvoid*)16);
295  glEnableVertexAttribArray(BONE_WEIGHT_LOCATION_GPU);
296  }
297 }
298 
300  return &m_aabb;
301 }
302 
303 void Mesh::createTriangles() {
304  for (unsigned int i = 0; i < m_chunks.size(); i++) {
305 
306  for (unsigned int j = 0; j + 2 < m_chunks[i].m_indices.size(); j += 3) {
307  triangles.push_back(createTriangleFromPositions(m_chunks[i].m_positions, m_chunks[i].m_indices, j));
308  }
309  }
310 }
311 
312 Triangle* Mesh::createTriangleFromPositions(std::vector<chag::float3> positionBuffer, std::vector<unsigned int> indices, unsigned int startIndex) {
313 
314  return new Triangle(make_vector(positionBuffer[indices[startIndex + 0]].x,
315  positionBuffer[indices[startIndex + 0]].y,
316  positionBuffer[indices[startIndex + 0]].z),
317  make_vector(positionBuffer[indices[startIndex + 1]].x,
318  positionBuffer[indices[startIndex + 1]].y,
319  positionBuffer[indices[startIndex + 1]].z),
320  make_vector(positionBuffer[indices[startIndex + 2]].x,
321  positionBuffer[indices[startIndex + 2]].y,
322  positionBuffer[indices[startIndex + 2]].z));
323 }
324 
325 std::vector<Triangle*> Mesh::getTriangles() {
326  return triangles;
327 }
328 
329 std::vector<Chunk>* Mesh::getChunks() {
330  return &m_chunks;
331 }
332 
333 std::vector<Material>* Mesh::getMaterials() {
334  return &materials;
335 }
336 
337 bool Mesh::hasAnimations() {
338  return numAnimations != 0;
339 }
340 
341 std::vector<float4x4> Mesh::getBoneTransforms(float totalElapsedTimeInSeconds) {
342  return boneTransformer->calculateBoneTransforms(totalElapsedTimeInSeconds);
343 }
Sphere getSphere()
Definition: Mesh.cpp:238
std::vector< Triangle * > getTriangles()
Definition: Mesh.cpp:325
std::vector< chag::float4x4 > getBoneTransforms(float totalElapsedTimeInSeconds)
Definition: Mesh.cpp:341
Definition: Chunk.h:27
Struct for maintaining information of how a bone affects a vertex.
Definition: Sphere.h:19
Definition: AABB2.h:23
AABB * getAABB()
Definition: Mesh.cpp:299