#pragma once #include "Mesh.h" #include "GameObject.h" #include #include #include "lmath.h" #include #include #include #include #include #include #include #include "MeshRenderer.h" using namespace lm; struct ObjVertex { int32_t vid; int32_t tid; int32_t nid; }; struct ObjFace { int32_t sid; int32_t mid; std::array vertices; }; class ObjGroup { public: ObjGroup(std::string objName) :name(objName), smoothGroupsEnabled(true), activeSmoothGroup(0) { } void AddFace(int32_t matId, const ObjVertex& v0, const ObjVertex& v1, const ObjVertex& v2) { ObjFace f; f.vertices[0] = v0; f.vertices[1] = v1; f.vertices[2] = v2; f.mid = matId; f.sid = activeSmoothGroup; faces.push_back(f); if (std::find(materials.begin(), materials.end(), matId) == materials.end()) { materials.push_back(matId); } } std::vector materials; std::string name; std::vector faces; int32_t activeSmoothGroup; bool smoothGroupsEnabled; }; class ObjMeshLoader { public: friend class ObjGroup; ObjMeshLoader() : _activeMatId(-1){ } std::string read_file(std::wstring path) { FILE* file = nullptr; auto err = _wfopen_s(&file, path.c_str(), L"rb"); fseek(file, 0, SEEK_END); auto file_size = ftell(file); std::string str; str.resize(file_size); fseek(file, 0, SEEK_SET); auto done = fread_s(&str[0], file_size, 1, file_size, file); fclose(file); return str; } std::vector Load(const std::wstring& path) { std::vector result; /*if (!file1.good()) { return result; }*/ auto str = read_file(path); std::stringstream buffer(str); std::vector line_buffer; line_buffer.resize(8192); std::string previous_line; std::string line; //while (buffer.getline(&line_buffer[0], line_buffer.size())) { while (std::getline(buffer, line)) { //std::string line(&line_buffer[0]); if (line.size() < 3) { continue; } switch(line[0]){ case 'v': switch (line[1]) { case ' ': { float3 v; sscanf_s(&line[2], "%f%f%f", &v.x(), &v.y(), &v.z()); _vertices.push_back(v); break; } case 'n': { float3 n; sscanf_s(&line[2], "%f%f%f", &n.x(), &n.y(), &n.z()); _normals.push_back(n); break; } case 't': float3 t; sscanf_s(&line[2], "%f%f%f", &t.x(), &t.y(), &t.z()); _texcoords.push_back(t); break; } break; case 'f': { bool is_open = false; size_t start = 2; int slash_pos_0 = 0; int slash_pos_1 = 0; if(line[line.size() - 1] == '\r') { line[line.size() - 1] = ' '; }else { line += " "; } std::vector fVertices; for (size_t i = 2; i < line.size(); ++i) { if (line[i] == ' ') { if (is_open) { //finalize chunk is_open = false; ObjVertex v; if (slash_pos_0 == -1) { auto pos2 = (int)buffer.tellg(); sscanf_s(&line[start], "%d", &v.vid); v.tid = v.nid = -1; } else if (slash_pos_1 == -1) { sscanf_s(&line[start], "%d/%d", &v.vid, &v.tid); v.nid = -1; } else { if (slash_pos_1 - slash_pos_0 == 1) { sscanf_s(&line[start], "%d//%d", &v.vid, &v.nid); v.tid = -1; } else { sscanf_s(&line[start], "%d/%d/%d", &v.vid, &v.tid, &v.nid); } } fVertices.push_back(v); } } else { if (!is_open) { start = i; slash_pos_0 = -1; slash_pos_1 = -1; is_open = true; } else { if (line[i] == '/') { if (slash_pos_0 == -1) { slash_pos_0 = i; } else { slash_pos_1 = i; } } } } } //emit triangles (split if quads) if (fVertices.size() == 3) { _groups[_groups.size() - 1].AddFace(_activeMatId, fVertices[0], fVertices[1], fVertices[2]); } else if (fVertices.size() == 4) { _groups[_groups.size() - 1].AddFace(_activeMatId, fVertices[0], fVertices[1], fVertices[2]); _groups[_groups.size() - 1].AddFace(_activeMatId, fVertices[0], fVertices[2], fVertices[3]); } break; } case 'g': { auto name = line.substr(2); _groups.push_back(ObjGroup(name)); break; } case 's': if (line.find("off") != std::string::npos) { _groups[_groups.size() - 1].smoothGroupsEnabled = false; } else { sscanf_s(&line[2], "%d", &_groups[_groups.size() - 1].activeSmoothGroup); } break; case 'm': { std::stringstream ss(line); std::string key, value; ss >> key; ss >> value; _mtlLibName = value; std::cout << "Found material library: " << value << std::endl; break; } case 'u': { std::stringstream ss(line); std::string key, value; ss >> key; ss >> value; SetActiveMaterial(value); break; } } previous_line = line; } auto mtllib_path = obj_to_mtl_path(path); _mats = load_mtllib(mtllib_path); for (auto& group : _groups) { result.push_back(UnpackGroup(group)); } return result; } private: std::vector _mats; std::wstring obj_to_mtl_path(const std::wstring& obj_path){ std::tr2::sys::path src(obj_path); auto dst = src.parent_path(); return dst.append(_mtlLibName).wstring(); } std::vector load_mtllib(const std::wstring& path){ std::vector result; auto text = std::ifstream(path); std::string line; auto default_shader = Shader::Create(L"shaders/default.fx"); MaterialPtr active_material; while(std::getline(text, line)) { if(line[0] == '#' || line.empty()){ continue; } std::string word; std::stringstream ls(line); ls >> word; if (word == "newmtl") { std::string mat_name; ls >> mat_name; if(active_material != nullptr) { result.push_back(active_material); } active_material = Material::Create(default_shader, mat_name); } else { if (active_material == nullptr) { continue; } else { if(word == "Kd") { std::vector color; float tmp = 0.0f; while(ls >> tmp) { color.push_back(tmp); } if(color.size() == 3) { active_material->GetParametersBlock().SetValue(Material::DiffuseColorName(), float4(color[0], color[1], color[2], 1.0f)); } } } } } if (active_material != nullptr) { result.push_back(active_material); } return result; } typedef std::tuple VertexPair; GameObjectPtr UnpackGroup(const ObjGroup& group) { std::vector> facesTable; facesTable.resize(_vertices.size()); std::vector vertices; std::map> submeshes; //EmitGeometry(group, vertices, submeshes); EmitGeometrySimple(group, vertices, submeshes); auto mesh = Mesh::Create(); mesh->SetSubmeshCount(group.materials.size()); mesh->SetVertices(vertices); int id = 0; for(auto& p : submeshes) { mesh->SetIndices(p.second, MeshTopology::TriangleList, id); id++; } auto obj = GameObject::create(); obj->AddComponent()->SetMesh(mesh); for (size_t i = 0; i < group.materials.size(); ++i){ auto mat_id = group.materials[i]; obj->GetComponent()->SetMaterial(_mats[mat_id], i); } mesh->RecalculateBounds(); return obj; } /*MeshPtr BuildMesh(ObjGroup& group) { std::vector vertices; std::vector indices; EmitGeometry(group, vertices, indices); auto mesh = Mesh::Create(); mesh->SetSubmeshCount(group.materials.size()); return mesh; }*/ void EmitGeometry(const ObjGroup& group, std::vector& vertices, std::map>& submeshes) { auto vComp = [](ObjVertex v1, ObjVertex v2) { return (v1.vid == v2.vid) && (v1.tid == v2.tid) && (v1.nid && v2.nid); }; std::vector tmpVertices; for (auto& f : group.faces) { for (auto& v : f.vertices) { int id = -1; for (size_t i = 0; i < tmpVertices.size(); ++i) { if (vComp(tmpVertices[i], v)) { id = i; break; } } if (id == -1) { id = tmpVertices.size(); tmpVertices.push_back(v); Vertex vertex; vertex.position = float4(_vertices[v.vid - 1], 1.0f); vertex.normal = _normals[v.nid - 1]; vertex.uv0 = _texcoords[v.tid - 1].xy(); vertices.push_back(vertex); } submeshes[f.mid].push_back(id); } } } void EmitGeometrySimple(const ObjGroup& group, std::vector& vertices, std::map>& submeshes) { int id = 0; for (auto& f : group.faces) { for (auto& v : f.vertices) { Vertex vertex; vertex.position = float4(_vertices[v.vid - 1], 1.0f); vertex.normal = _normals[v.nid - 1]; vertex.uv0 = _texcoords[v.tid - 1].xy(); vertices.push_back(vertex); submeshes[f.mid].push_back(id); ++id; } } } void SetActiveMaterial(const std::string& name) { for (size_t i = 0; i < _materials.size(); ++i) { if (_materials[i] == name) { _activeMatId = i; return; } } _activeMatId = _materials.size(); _materials.push_back(name); } int _activeMatId; std::string _mtlLibName; std::vector _vertices; std::vector _normals; std::vector _texcoords; std::vector _materials; std::vector _groups; };