// ---------------------------------------------------------------- // From Game Programming in C++ by Sanjay Madhav // Copyright (C) 2017 Sanjay Madhav. All rights reserved. // // Released under the BSD License // See LICENSE in root directory for full details. // ---------------------------------------------------------------- #include "Mesh.h" #include "Renderer.h" #include "Texture.h" #include "VertexArray.h" #include #include #include #include #include "Math.h" namespace { union Vertex { float f; uint8_t b[4]; }; } Mesh::Mesh() :mBox(Vector3::Infinity, Vector3::NegInfinity) ,mVertexArray(nullptr) ,mRadius(0.0f) ,mSpecPower(100.0f) { } Mesh::~Mesh() { } bool Mesh::Load(const std::string& fileName, Renderer* renderer) { std::ifstream file(fileName); if (!file.is_open()) { SDL_Log("File not found: Mesh %s", fileName.c_str()); return false; } std::stringstream fileStream; fileStream << file.rdbuf(); std::string contents = fileStream.str(); rapidjson::StringStream jsonStr(contents.c_str()); rapidjson::Document doc; doc.ParseStream(jsonStr); if (!doc.IsObject()) { SDL_Log("Mesh %s is not valid json", fileName.c_str()); return false; } int ver = doc["version"].GetInt(); // Check the version if (ver != 1) { SDL_Log("Mesh %s not version 1", fileName.c_str()); return false; } mShaderName = doc["shader"].GetString(); // Set the vertex layout/size based on the format in the file VertexArray::Layout layout = VertexArray::PosNormTex; size_t vertSize = 8; std::string vertexFormat = doc["vertexformat"].GetString(); if (vertexFormat == "PosNormSkinTex") { layout = VertexArray::PosNormSkinTex; // This is the number of "Vertex" unions, which is 8 + 2 (for skinning)s vertSize = 10; } // Load textures const rapidjson::Value& textures = doc["textures"]; if (!textures.IsArray() || textures.Size() < 1) { SDL_Log("Mesh %s has no textures, there should be at least one", fileName.c_str()); return false; } mSpecPower = static_cast(doc["specularPower"].GetDouble()); for (rapidjson::SizeType i = 0; i < textures.Size(); i++) { // Is this texture already loaded? std::string texName = textures[i].GetString(); Texture* t = renderer->GetTexture(texName); if (t == nullptr) { // Try loading the texture t = renderer->GetTexture(texName); if (t == nullptr) { // If it's still null, just use the default texture t = renderer->GetTexture("Assets/Default.png"); } } mTextures.emplace_back(t); } // Load in the vertices const rapidjson::Value& vertsJson = doc["vertices"]; if (!vertsJson.IsArray() || vertsJson.Size() < 1) { SDL_Log("Mesh %s has no vertices", fileName.c_str()); return false; } std::vector vertices; vertices.reserve(vertsJson.Size() * vertSize); mRadius = 0.0f; for (rapidjson::SizeType i = 0; i < vertsJson.Size(); i++) { // For now, just assume we have 8 elements const rapidjson::Value& vert = vertsJson[i]; if (!vert.IsArray()) { SDL_Log("Unexpected vertex format for %s", fileName.c_str()); return false; } Vector3 pos(vert[0].GetDouble(), vert[1].GetDouble(), vert[2].GetDouble()); mRadius = Math::Max(mRadius, pos.LengthSq()); mBox.UpdateMinMax(pos); if (layout == VertexArray::PosNormTex) { Vertex v; // Add the floats for (rapidjson::SizeType j = 0; j < vert.Size(); j++) { v.f = static_cast(vert[j].GetDouble()); vertices.emplace_back(v); } } else { Vertex v; // Add pos/normal for (rapidjson::SizeType j = 0; j < 6; j++) { v.f = static_cast(vert[j].GetDouble()); vertices.emplace_back(v); } // Add skin information for (rapidjson::SizeType j = 6; j < 14; j += 4) { v.b[0] = vert[j].GetUint(); v.b[1] = vert[j + 1].GetUint(); v.b[2] = vert[j + 2].GetUint(); v.b[3] = vert[j + 3].GetUint(); vertices.emplace_back(v); } // Add tex coords for (rapidjson::SizeType j = 14; j < vert.Size(); j++) { v.f = vert[j].GetDouble(); vertices.emplace_back(v); } } } // We were computing length squared earlier mRadius = Math::Sqrt(mRadius); // Load in the indices const rapidjson::Value& indJson = doc["indices"]; if (!indJson.IsArray() || indJson.Size() < 1) { SDL_Log("Mesh %s has no indices", fileName.c_str()); return false; } std::vector indices; indices.reserve(indJson.Size() * 3); for (rapidjson::SizeType i = 0; i < indJson.Size(); i++) { const rapidjson::Value& ind = indJson[i]; if (!ind.IsArray() || ind.Size() != 3) { SDL_Log("Invalid indices for %s", fileName.c_str()); return false; } indices.emplace_back(ind[0].GetUint()); indices.emplace_back(ind[1].GetUint()); indices.emplace_back(ind[2].GetUint()); } // Now create a vertex array mVertexArray = new VertexArray(vertices.data(), static_cast(vertices.size()) / vertSize, layout, indices.data(), static_cast(indices.size())); return true; } void Mesh::Unload() { delete mVertexArray; mVertexArray = nullptr; } Texture* Mesh::GetTexture(size_t index) { if (index < mTextures.size()) { return mTextures[index]; } else { return nullptr; } }