2
0

Mesh.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. // ----------------------------------------------------------------
  2. // From Game Programming in C++ by Sanjay Madhav
  3. // Copyright (C) 2017 Sanjay Madhav. All rights reserved.
  4. //
  5. // Released under the BSD License
  6. // See LICENSE in root directory for full details.
  7. // ----------------------------------------------------------------
  8. #include "Mesh.h"
  9. #include "Renderer.h"
  10. #include "Texture.h"
  11. #include "VertexArray.h"
  12. #include <rapidjson/document.h>
  13. #include <SDL/SDL_log.h>
  14. #include "Math.h"
  15. #include "LevelLoader.h"
  16. #include <fstream>
  17. namespace
  18. {
  19. union Vertex
  20. {
  21. float f;
  22. uint8_t b[4];
  23. };
  24. const int BinaryVersion = 1;
  25. struct MeshBinHeader
  26. {
  27. // Signature for file type
  28. char mSignature[4] = { 'G', 'M', 'S', 'H' };
  29. // Version
  30. uint32_t mVersion = BinaryVersion;
  31. // Vertex layout type
  32. VertexArray::Layout mLayout = VertexArray::PosNormTex;
  33. // Info about how many of each we have
  34. uint32_t mNumTextures = 0;
  35. uint32_t mNumVerts = 0;
  36. uint32_t mNumIndices = 0;
  37. // Box/radius of mesh, used for collision
  38. AABB mBox{ Vector3::Zero, Vector3::Zero };
  39. float mRadius = 0.0f;
  40. float mSpecPower = 100.0f;
  41. };
  42. }
  43. Mesh::Mesh()
  44. :mBox(Vector3::Infinity, Vector3::NegInfinity)
  45. ,mVertexArray(nullptr)
  46. ,mRadius(0.0f)
  47. ,mSpecPower(100.0f)
  48. {
  49. }
  50. Mesh::~Mesh()
  51. {
  52. }
  53. bool Mesh::Load(const std::string& fileName, Renderer* renderer)
  54. {
  55. mFileName = fileName;
  56. // Try loading the binary file first
  57. if (LoadBinary(fileName + ".bin", renderer))
  58. {
  59. return true;
  60. }
  61. rapidjson::Document doc;
  62. if (!LevelLoader::LoadJSON(fileName, doc))
  63. {
  64. SDL_Log("Failed to load mesh %s", fileName.c_str());
  65. return false;
  66. }
  67. int ver = doc["version"].GetInt();
  68. // Check the version
  69. if (ver != 1)
  70. {
  71. SDL_Log("Mesh %s not version 1", fileName.c_str());
  72. return false;
  73. }
  74. mShaderName = doc["shader"].GetString();
  75. // Set the vertex layout/size based on the format in the file
  76. VertexArray::Layout layout = VertexArray::PosNormTex;
  77. size_t vertSize = 8;
  78. std::string vertexFormat = doc["vertexformat"].GetString();
  79. if (vertexFormat == "PosNormSkinTex")
  80. {
  81. layout = VertexArray::PosNormSkinTex;
  82. // This is the number of "Vertex" unions, which is 8 + 2 (for skinning)s
  83. vertSize = 10;
  84. }
  85. // Load textures
  86. const rapidjson::Value& textures = doc["textures"];
  87. if (!textures.IsArray() || textures.Size() < 1)
  88. {
  89. SDL_Log("Mesh %s has no textures, there should be at least one", fileName.c_str());
  90. return false;
  91. }
  92. mSpecPower = static_cast<float>(doc["specularPower"].GetDouble());
  93. std::vector<std::string> textureNames;
  94. for (rapidjson::SizeType i = 0; i < textures.Size(); i++)
  95. {
  96. // Is this texture already loaded?
  97. std::string texName = textures[i].GetString();
  98. textureNames.emplace_back(texName);
  99. Texture* t = renderer->GetTexture(texName);
  100. if (t == nullptr)
  101. {
  102. // If it's null, use the default texture
  103. t = renderer->GetTexture("Assets/Default.png");
  104. }
  105. mTextures.emplace_back(t);
  106. }
  107. // Load in the vertices
  108. const rapidjson::Value& vertsJson = doc["vertices"];
  109. if (!vertsJson.IsArray() || vertsJson.Size() < 1)
  110. {
  111. SDL_Log("Mesh %s has no vertices", fileName.c_str());
  112. return false;
  113. }
  114. std::vector<Vertex> vertices;
  115. vertices.reserve(vertsJson.Size() * vertSize);
  116. mRadius = 0.0f;
  117. for (rapidjson::SizeType i = 0; i < vertsJson.Size(); i++)
  118. {
  119. // For now, just assume we have 8 elements
  120. const rapidjson::Value& vert = vertsJson[i];
  121. if (!vert.IsArray())
  122. {
  123. SDL_Log("Unexpected vertex format for %s", fileName.c_str());
  124. return false;
  125. }
  126. Vector3 pos(vert[0].GetDouble(), vert[1].GetDouble(), vert[2].GetDouble());
  127. mRadius = Math::Max(mRadius, pos.LengthSq());
  128. mBox.UpdateMinMax(pos);
  129. if (layout == VertexArray::PosNormTex)
  130. {
  131. Vertex v;
  132. // Add the floats
  133. for (rapidjson::SizeType j = 0; j < vert.Size(); j++)
  134. {
  135. v.f = static_cast<float>(vert[j].GetDouble());
  136. vertices.emplace_back(v);
  137. }
  138. }
  139. else
  140. {
  141. Vertex v;
  142. // Add pos/normal
  143. for (rapidjson::SizeType j = 0; j < 6; j++)
  144. {
  145. v.f = static_cast<float>(vert[j].GetDouble());
  146. vertices.emplace_back(v);
  147. }
  148. // Add skin information
  149. for (rapidjson::SizeType j = 6; j < 14; j += 4)
  150. {
  151. v.b[0] = vert[j].GetUint();
  152. v.b[1] = vert[j + 1].GetUint();
  153. v.b[2] = vert[j + 2].GetUint();
  154. v.b[3] = vert[j + 3].GetUint();
  155. vertices.emplace_back(v);
  156. }
  157. // Add tex coords
  158. for (rapidjson::SizeType j = 14; j < vert.Size(); j++)
  159. {
  160. v.f = vert[j].GetDouble();
  161. vertices.emplace_back(v);
  162. }
  163. }
  164. }
  165. // We were computing length squared earlier
  166. mRadius = Math::Sqrt(mRadius);
  167. // Load in the indices
  168. const rapidjson::Value& indJson = doc["indices"];
  169. if (!indJson.IsArray() || indJson.Size() < 1)
  170. {
  171. SDL_Log("Mesh %s has no indices", fileName.c_str());
  172. return false;
  173. }
  174. std::vector<unsigned int> indices;
  175. indices.reserve(indJson.Size() * 3);
  176. for (rapidjson::SizeType i = 0; i < indJson.Size(); i++)
  177. {
  178. const rapidjson::Value& ind = indJson[i];
  179. if (!ind.IsArray() || ind.Size() != 3)
  180. {
  181. SDL_Log("Invalid indices for %s", fileName.c_str());
  182. return false;
  183. }
  184. indices.emplace_back(ind[0].GetUint());
  185. indices.emplace_back(ind[1].GetUint());
  186. indices.emplace_back(ind[2].GetUint());
  187. }
  188. // Now create a vertex array
  189. unsigned int numVerts = static_cast<unsigned>(vertices.size()) / vertSize;
  190. mVertexArray = new VertexArray(vertices.data(), numVerts,
  191. layout, indices.data(), static_cast<unsigned>(indices.size()));
  192. // Save the binary mesh
  193. SaveBinary(fileName + ".bin", vertices.data(),
  194. numVerts, layout, indices.data(),
  195. static_cast<unsigned>(indices.size()),
  196. textureNames, mBox, mRadius,
  197. mSpecPower);
  198. return true;
  199. }
  200. void Mesh::Unload()
  201. {
  202. delete mVertexArray;
  203. mVertexArray = nullptr;
  204. }
  205. Texture* Mesh::GetTexture(size_t index)
  206. {
  207. if (index < mTextures.size())
  208. {
  209. return mTextures[index];
  210. }
  211. else
  212. {
  213. return nullptr;
  214. }
  215. }
  216. void Mesh::SaveBinary(const std::string& fileName, const void* verts,
  217. uint32_t numVerts, VertexArray::Layout layout,
  218. const uint32_t* indices, uint32_t numIndices,
  219. const std::vector<std::string>& textureNames,
  220. const AABB& box, float radius,
  221. float specPower)
  222. {
  223. // Create header struct
  224. MeshBinHeader header;
  225. header.mLayout = layout;
  226. header.mNumTextures =
  227. static_cast<unsigned>(textureNames.size());
  228. header.mNumVerts = numVerts;
  229. header.mNumIndices = numIndices;
  230. header.mBox = box;
  231. header.mRadius = radius;
  232. // Open binary file for writing
  233. std::ofstream outFile(fileName, std::ios::out
  234. | std::ios::binary);
  235. if (outFile.is_open())
  236. {
  237. // Write the header
  238. outFile.write(reinterpret_cast<char*>(&header), sizeof(header));
  239. // For each texture, we need to write the size of the name
  240. // followed by the string (null-terminated)
  241. for (const auto& tex : textureNames)
  242. {
  243. // (Assume file names won't have more than 32k characters)
  244. uint16_t nameSize = static_cast<uint16_t>(tex.length()) + 1;
  245. outFile.write(reinterpret_cast<char*>(&nameSize), sizeof(nameSize));
  246. outFile.write(tex.c_str(), nameSize - 1);
  247. outFile.write("\0", 1);
  248. }
  249. // Figure out number of bytes for each vertex, based on layout
  250. unsigned vertexSize = VertexArray::GetVertexSize(layout);
  251. // Write vertices
  252. outFile.write(reinterpret_cast<const char*>(verts),
  253. numVerts * vertexSize);
  254. // Write indices
  255. outFile.write(reinterpret_cast<const char*>(indices),
  256. numIndices * sizeof(uint32_t));
  257. }
  258. }
  259. bool Mesh::LoadBinary(const std::string& fileName, Renderer* renderer)
  260. {
  261. std::ifstream inFile(fileName, std::ios::in |
  262. std::ios::binary);
  263. if (inFile.is_open())
  264. {
  265. // Read in header
  266. MeshBinHeader header;
  267. inFile.read(reinterpret_cast<char*>(&header), sizeof(header));
  268. // Validate the header signature and version
  269. char* sig = header.mSignature;
  270. if (sig[0] != 'G' || sig[1] != 'M' || sig[2] != 'S' ||
  271. sig[3] != 'H' || header.mVersion != BinaryVersion)
  272. {
  273. return false;
  274. }
  275. // Read in the texture file names
  276. for (uint32_t i = 0; i < header.mNumTextures; i++)
  277. {
  278. // Get the file name size
  279. uint16_t nameSize = 0;
  280. inFile.read(reinterpret_cast<char*>(&nameSize), sizeof(nameSize));
  281. // Make a buffer of this size
  282. char* texName = new char[nameSize];
  283. // Read in the texture name
  284. inFile.read(texName, nameSize);
  285. // Get this texture
  286. Texture* t = renderer->GetTexture(texName);
  287. if (t == nullptr)
  288. {
  289. // If it's null, use the default texture
  290. t = renderer->GetTexture("Assets/Default.png");
  291. }
  292. mTextures.emplace_back(t);
  293. delete[] texName;
  294. }
  295. // Now read in the vertices
  296. unsigned vertexSize = VertexArray::GetVertexSize(header.mLayout);
  297. char* verts = new char[header.mNumVerts * vertexSize];
  298. inFile.read(verts, header.mNumVerts * vertexSize);
  299. // Now read in the indices
  300. uint32_t* indices = new uint32_t[header.mNumIndices];
  301. inFile.read(reinterpret_cast<char*>(indices),
  302. header.mNumIndices * sizeof(uint32_t));
  303. // Now create the vertex array
  304. mVertexArray = new VertexArray(verts, header.mNumVerts,
  305. header.mLayout, indices, header.mNumIndices);
  306. // Cleanup memory
  307. delete[] verts;
  308. delete[] indices;
  309. // Set mBox/mRadius/specular from header
  310. mBox = header.mBox;
  311. mRadius = header.mRadius;
  312. mSpecPower = header.mSpecPower;
  313. return true;
  314. }
  315. return false;
  316. }