From 93e950584a9b19ddee708de325a7d9bc8c5e8ed7 Mon Sep 17 00:00:00 2001 From: Piotr Dziwinski Date: Thu, 9 Jul 2015 18:45:02 +0200 Subject: [PATCH] Refactor model-related code * refactored model loading code based on code from dev-models * support new model format V2 (without LOD levels) * removed LOD levels support in CEngine and from model files * preparations for new model format V3: - support for multiple meshes in one model file - support for saving crash spheres and shadow spots - removed all direct dependencies on CEngine enum values - quantized model rendering states to new flags and enums --- data | 2 +- src/CMakeLists.txt | 8 +- src/app/app.cpp | 1 - src/common/ioutils.h | 8 +- src/graphics/engine/engine.cpp | 419 ++---- src/graphics/engine/engine.h | 53 +- src/graphics/engine/modelfile.cpp | 1249 ----------------- src/graphics/engine/modelfile.h | 159 --- .../{modelmanager.cpp => oldmodelmanager.cpp} | 146 +- .../{modelmanager.h => oldmodelmanager.h} | 16 +- src/graphics/engine/pyro.cpp | 11 +- src/graphics/engine/terrain.cpp | 2 +- src/graphics/model/model.cpp | 60 + src/graphics/model/model.h | 46 + src/graphics/model/model_crash_sphere.h | 19 + src/graphics/model/model_format.h | 16 + src/graphics/model/model_input.cpp | 670 +++++++++ src/graphics/model/model_input.h | 23 + src/graphics/model/model_io_exception.h | 15 + src/graphics/model/model_io_structs.h | 243 ++++ src/graphics/model/model_manager.cpp | 32 + src/graphics/model/model_manager.h | 28 + src/graphics/model/model_mesh.cpp | 65 + src/graphics/model/model_mesh.h | 57 + src/graphics/model/model_output.cpp | 338 +++++ src/graphics/model/model_output.h | 21 + src/graphics/model/model_shadow_spot.h | 15 + src/graphics/model/model_triangle.h | 68 + src/object/auto/autoportico.cpp | 4 +- src/object/motion/motion.h | 2 +- src/object/motion/motionant.cpp | 4 +- src/object/motion/motionant.h | 2 +- src/object/motion/motionbee.cpp | 4 +- src/object/motion/motionbee.h | 2 +- src/object/motion/motiondummy.cpp | 4 +- src/object/motion/motiondummy.h | 2 +- src/object/motion/motionhuman.cpp | 4 +- src/object/motion/motionhuman.h | 2 +- src/object/motion/motionmother.cpp | 4 +- src/object/motion/motionmother.h | 2 +- src/object/motion/motionspider.cpp | 4 +- src/object/motion/motionspider.h | 2 +- src/object/motion/motiontoto.cpp | 4 +- src/object/motion/motiontoto.h | 2 +- src/object/motion/motionvehicle.cpp | 21 +- src/object/motion/motionvehicle.h | 2 +- src/object/motion/motionworm.cpp | 4 +- src/object/motion/motionworm.h | 2 +- src/object/object_factory.cpp | 4 +- src/object/object_factory.h | 6 +- src/object/object_manager.cpp | 2 +- src/object/object_manager.h | 4 +- src/object/old_object.cpp | 14 +- src/object/robotmain.cpp | 2 +- src/object/robotmain.h | 2 +- src/object/subclass/exchange_post.cpp | 4 +- src/object/subclass/exchange_post.h | 4 +- src/tools/CMakeLists.txt | 12 +- src/tools/convert_model.cpp | 186 +-- 59 files changed, 2132 insertions(+), 1975 deletions(-) delete mode 100644 src/graphics/engine/modelfile.cpp delete mode 100644 src/graphics/engine/modelfile.h rename src/graphics/engine/{modelmanager.cpp => oldmodelmanager.cpp} (52%) rename src/graphics/engine/{modelmanager.h => oldmodelmanager.h} (92%) create mode 100644 src/graphics/model/model.cpp create mode 100644 src/graphics/model/model.h create mode 100644 src/graphics/model/model_crash_sphere.h create mode 100644 src/graphics/model/model_format.h create mode 100644 src/graphics/model/model_input.cpp create mode 100644 src/graphics/model/model_input.h create mode 100644 src/graphics/model/model_io_exception.h create mode 100644 src/graphics/model/model_io_structs.h create mode 100644 src/graphics/model/model_manager.cpp create mode 100644 src/graphics/model/model_manager.h create mode 100644 src/graphics/model/model_mesh.cpp create mode 100644 src/graphics/model/model_mesh.h create mode 100644 src/graphics/model/model_output.cpp create mode 100644 src/graphics/model/model_output.h create mode 100644 src/graphics/model/model_shadow_spot.h create mode 100644 src/graphics/model/model_triangle.h diff --git a/data b/data index 73bb8f9a..c1da1d3c 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 73bb8f9ac0f8af3e810455af2ab2dd5113af03eb +Subproject commit c1da1d3ca64d8b6450023d9a6f095b5e8a80da63 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 145130b5..017bcb51 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -104,8 +104,7 @@ set(BASE_SOURCES graphics/engine/engine.cpp graphics/engine/lightman.cpp graphics/engine/lightning.cpp - graphics/engine/modelfile.cpp - graphics/engine/modelmanager.cpp + graphics/engine/oldmodelmanager.cpp graphics/engine/particle.cpp graphics/engine/planet.cpp graphics/engine/pyro.cpp @@ -118,6 +117,11 @@ set(BASE_SOURCES graphics/opengl/gldevice.cpp graphics/opengl/glframebuffer.cpp graphics/opengl/glutil.cpp + graphics/model/model.cpp + graphics/model/model_input.cpp + graphics/model/model_manager.cpp + graphics/model/model_mesh.cpp + graphics/model/model_output.cpp object/auto/auto.cpp object/auto/autobase.cpp object/auto/autoconvert.cpp diff --git a/src/app/app.cpp b/src/app/app.cpp index e3814d50..0942cc1b 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -32,7 +32,6 @@ #include "common/stringutils.h" #include "common/resources/resourcemanager.h" -#include "graphics/engine/modelmanager.h" #include "graphics/core/nulldevice.h" #include "graphics/opengl/glutil.h" diff --git a/src/common/ioutils.h b/src/common/ioutils.h index 12c29431..4b5a27be 100644 --- a/src/common/ioutils.h +++ b/src/common/ioutils.h @@ -70,7 +70,7 @@ T ReadBinary(std::istream &istr) /** * false is 0; true is 1. */ -void WriteBinaryBool(float value, std::ostream &ostr) +inline void WriteBinaryBool(float value, std::ostream &ostr) { unsigned char v = value ? 1 : 0; IOUtils::WriteBinary<1, unsigned char>(v, ostr); @@ -80,7 +80,7 @@ void WriteBinaryBool(float value, std::ostream &ostr) /** * 0 is false; other values are true. */ -bool ReadBinaryBool(std::istream &istr) +inline bool ReadBinaryBool(std::istream &istr) { int v = IOUtils::ReadBinary<1, unsigned char>(istr); return v != 0; @@ -91,7 +91,7 @@ bool ReadBinaryBool(std::istream &istr) * Write order is little-endian * NOTE: code is probably not portable as there are platforms with other float representations. */ -void WriteBinaryFloat(float value, std::ostream &ostr) +inline void WriteBinaryFloat(float value, std::ostream &ostr) { union { float fValue; unsigned int iValue; } u; memset(&u, 0, sizeof(u)); @@ -104,7 +104,7 @@ void WriteBinaryFloat(float value, std::ostream &ostr) * Read order is little-endian * NOTE: code is probably not portable as there are platforms with other float representations. */ -float ReadBinaryFloat(std::istream &istr) +inline float ReadBinaryFloat(std::istream &istr) { union { float fValue; unsigned int iValue; } u; memset(&u, 0, sizeof(u)); diff --git a/src/graphics/engine/engine.cpp b/src/graphics/engine/engine.cpp index b10dd818..2e3605d7 100644 --- a/src/graphics/engine/engine.cpp +++ b/src/graphics/engine/engine.cpp @@ -32,7 +32,7 @@ #include "graphics/engine/cloud.h" #include "graphics/engine/lightman.h" #include "graphics/engine/lightning.h" -#include "graphics/engine/modelmanager.h" +#include "graphics/engine/oldmodelmanager.h" #include "graphics/engine/particle.h" #include "graphics/engine/planet.h" #include "graphics/engine/pyro_manager.h" @@ -240,7 +240,7 @@ CDevice* CEngine::GetDevice() return m_device; } -CModelManager* CEngine::GetModelManager() +COldModelManager* CEngine::GetModelManager() { return m_modelManager.get(); } @@ -299,7 +299,7 @@ bool CEngine::Create() { m_size = m_app->GetVideoConfig().size; - m_modelManager.reset(new CModelManager(this)); + m_modelManager.reset(new COldModelManager(this)); m_pyroManager.reset(new CPyroManager()); m_lightMan = new CLightManager(this); m_text = new CText(this); @@ -595,19 +595,7 @@ EngineBaseObjTexTier& CEngine::AddLevel2(EngineBaseObject& p1, const std::string return p1.next.back(); } -EngineBaseObjLODTier& CEngine::AddLevel3(EngineBaseObjTexTier& p2, LODLevel lodLevel) -{ - for (int i = 0; i < static_cast( p2.next.size() ); i++) - { - if (p2.next[i].lodLevel == lodLevel) - return p2.next[i]; - } - - p2.next.push_back(EngineBaseObjLODTier(lodLevel)); - return p2.next.back(); -} - -EngineBaseObjDataTier& CEngine::AddLevel4(EngineBaseObjLODTier& p3, EngineTriangleType type, +EngineBaseObjDataTier& CEngine::AddLevel3(EngineBaseObjTexTier& p3, EngineTriangleType type, const Material& material, int state) { for (int i = 0; i < static_cast( p3.next.size() ); i++) @@ -658,15 +646,9 @@ void CEngine::DeleteBaseObject(int baseObjRank) for (int l3 = 0; l3 < static_cast( p2.next.size() ); l3++) { - EngineBaseObjLODTier& p3 = p2.next[l3]; - - for (int l4 = 0; l4 < static_cast( p3.next.size() ); l4++) - { - EngineBaseObjDataTier& p4 = p3.next[l4]; - - m_device->DestroyStaticBuffer(p4.staticBufferId); - p4.staticBufferId = 0; - } + EngineBaseObjDataTier& p3 = p2.next[l3]; + m_device->DestroyStaticBuffer(p3.staticBufferId); + p3.staticBufferId = 0; } } @@ -689,15 +671,9 @@ void CEngine::DeleteAllBaseObjects() for (int l3 = 0; l3 < static_cast( p2.next.size() ); l3++) { - EngineBaseObjLODTier& p3 = p2.next[l3]; - - for (int l4 = 0; l4 < static_cast( p3.next.size() ); l4++) - { - EngineBaseObjDataTier& p4 = p3.next[l4]; - - m_device->DestroyStaticBuffer(p4.staticBufferId); - p4.staticBufferId = 0; - } + EngineBaseObjDataTier& p3 = p2.next[l3]; + m_device->DestroyStaticBuffer(p3.staticBufferId); + p3.staticBufferId = 0; } } } @@ -723,13 +699,8 @@ void CEngine::CopyBaseObject(int sourceBaseObjRank, int destBaseObjRank) for (int l3 = 0; l3 < static_cast( p2.next.size() ); l3++) { - EngineBaseObjLODTier& p3 = p2.next[l3]; - - for (int l4 = 0; l4 < static_cast( p3.next.size() ); l4++) - { - EngineBaseObjDataTier& p4 = p3.next[l4]; - p4.staticBufferId = 0; - } + EngineBaseObjDataTier& p3 = p2.next[l3]; + p3.staticBufferId = 0; } } } @@ -738,18 +709,17 @@ void CEngine::AddBaseObjTriangles(int baseObjRank, const std::vector EngineTriangleType triangleType, const Material& material, int state, std::string tex1Name, std::string tex2Name, - LODLevel lodLevel, bool globalUpdate) + bool globalUpdate) { assert(baseObjRank >= 0 && baseObjRank < static_cast( m_baseObjects.size() )); EngineBaseObject& p1 = m_baseObjects[baseObjRank]; EngineBaseObjTexTier& p2 = AddLevel2(p1, tex1Name, tex2Name); - EngineBaseObjLODTier& p3 = AddLevel3(p2, lodLevel); - EngineBaseObjDataTier& p4 = AddLevel4(p3, triangleType, material, state); + EngineBaseObjDataTier& p3 = AddLevel3(p2, triangleType, material, state); - p4.vertices.insert(p4.vertices.end(), vertices.begin(), vertices.end()); + p3.vertices.insert(p3.vertices.end(), vertices.begin(), vertices.end()); - p4.updateStaticBuffer = true; + p3.updateStaticBuffer = true; m_updateStaticBuffers = true; if (globalUpdate) @@ -779,19 +749,18 @@ void CEngine::AddBaseObjTriangles(int baseObjRank, const std::vector void CEngine::AddBaseObjQuick(int baseObjRank, const EngineBaseObjDataTier& buffer, std::string tex1Name, std::string tex2Name, - LODLevel lodLevel, bool globalUpdate) + bool globalUpdate) { assert(baseObjRank >= 0 && baseObjRank < static_cast( m_baseObjects.size() )); EngineBaseObject& p1 = m_baseObjects[baseObjRank]; EngineBaseObjTexTier& p2 = AddLevel2(p1, tex1Name, tex2Name); - EngineBaseObjLODTier& p3 = AddLevel3(p2, lodLevel); - p3.next.push_back(buffer); + p2.next.push_back(buffer); - EngineBaseObjDataTier& p4 = p3.next.back(); + EngineBaseObjDataTier& p3 = p2.next.back(); - UpdateStaticBuffer(p4); + UpdateStaticBuffer(p3); if (globalUpdate) { @@ -799,23 +768,23 @@ void CEngine::AddBaseObjQuick(int baseObjRank, const EngineBaseObjDataTier& buff } else { - for (int i = 0; i < static_cast( p4.vertices.size() ); i++) + for (int i = 0; i < static_cast( p3.vertices.size() ); i++) { - p1.bboxMin.x = Math::Min(p4.vertices[i].coord.x, p1.bboxMin.x); - p1.bboxMin.y = Math::Min(p4.vertices[i].coord.y, p1.bboxMin.y); - p1.bboxMin.z = Math::Min(p4.vertices[i].coord.z, p1.bboxMin.z); - p1.bboxMax.x = Math::Max(p4.vertices[i].coord.x, p1.bboxMax.x); - p1.bboxMax.y = Math::Max(p4.vertices[i].coord.y, p1.bboxMax.y); - p1.bboxMax.z = Math::Max(p4.vertices[i].coord.z, p1.bboxMax.z); + p1.bboxMin.x = Math::Min(p3.vertices[i].coord.x, p1.bboxMin.x); + p1.bboxMin.y = Math::Min(p3.vertices[i].coord.y, p1.bboxMin.y); + p1.bboxMin.z = Math::Min(p3.vertices[i].coord.z, p1.bboxMin.z); + p1.bboxMax.x = Math::Max(p3.vertices[i].coord.x, p1.bboxMax.x); + p1.bboxMax.y = Math::Max(p3.vertices[i].coord.y, p1.bboxMax.y); + p1.bboxMax.z = Math::Max(p3.vertices[i].coord.z, p1.bboxMax.z); } p1.radius = Math::Max(p1.bboxMin.Length(), p1.bboxMax.Length()); } - if (p4.type == ENG_TRIANGLE_TYPE_TRIANGLES) - p1.totalTriangles += p4.vertices.size() / 3; - else if (p4.type == ENG_TRIANGLE_TYPE_SURFACE) - p1.totalTriangles += p4.vertices.size() - 2; + if (p3.type == ENG_TRIANGLE_TYPE_TRIANGLES) + p1.totalTriangles += p3.vertices.size() / 3; + else if (p3.type == ENG_TRIANGLE_TYPE_SURFACE) + p1.totalTriangles += p3.vertices.size() - 2; } void CEngine::DebugObject(int objRank) @@ -876,21 +845,13 @@ void CEngine::DebugObject(int objRank) for (int l3 = 0; l3 < static_cast( p2.next.size() ); l3++) { - EngineBaseObjLODTier& p3 = p2.next[l3]; + EngineBaseObjDataTier& p3 = p2.next[l3]; - l->Debug(" l3:\n"); - l->Debug(" lodLevel: %d\n", p3.lodLevel); - - for (int l4 = 0; l4 < static_cast( p3.next.size() ); l4++) - { - EngineBaseObjDataTier& p4 = p3.next[l4]; - - l->Debug(" l4:\n"); - l->Debug(" type: %d\n", p4.type); - l->Debug(" state: %d\n", p4.state); - l->Debug(" staticBufferId: %u\n", p4.staticBufferId); - l->Debug(" updateStaticBuffer: %s\n", p4.updateStaticBuffer ? "true" : "false"); - } + l->Debug(" l3:\n"); + l->Debug(" type: %d\n", p3.type); + l->Debug(" state: %d\n", p3.state); + l->Debug(" staticBufferId: %u\n", p3.staticBufferId); + l->Debug(" updateStaticBuffer: %s\n", p3.updateStaticBuffer ? "true" : "false"); } } } @@ -1038,7 +999,7 @@ int CEngine::GetObjectTotalTriangles(int objRank) EngineBaseObjDataTier* CEngine::FindTriangles(int objRank, const Material& material, int state, std::string tex1Name, - std::string tex2Name, int lodLevelMask) + std::string tex2Name) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); @@ -1059,28 +1020,20 @@ EngineBaseObjDataTier* CEngine::FindTriangles(int objRank, const Material& mater for (int l3 = 0; l3 < static_cast( p2.next.size() ); l3++) { - EngineBaseObjLODTier& p3 = p2.next[l3]; + EngineBaseObjDataTier& p3 = p2.next[l3]; - if ((p3.lodLevel & lodLevelMask) == 0) + if ( (p3.state & (~(ENG_RSTATE_DUAL_BLACK|ENG_RSTATE_DUAL_WHITE))) != state || + p3.material != material ) continue; - for (int l4 = 0; l4 < static_cast( p3.next.size() ); l4++) - { - EngineBaseObjDataTier& p4 = p3.next[l4]; - - if ( (p4.state & (~(ENG_RSTATE_DUAL_BLACK|ENG_RSTATE_DUAL_WHITE))) != state || - p4.material != material ) - continue; - - return &p4; - } + return &p3; } } return nullptr; } -int CEngine::GetPartialTriangles(int objRank, int lodLevelMask, float percent, int maxCount, +int CEngine::GetPartialTriangles(int objRank, float percent, int maxCount, std::vector& triangles) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); @@ -1105,62 +1058,54 @@ int CEngine::GetPartialTriangles(int objRank, int lodLevelMask, float percent, i for (int l3 = 0; l3 < static_cast( p2.next.size() ); l3++) { - EngineBaseObjLODTier& p3 = p2.next[l3]; + EngineBaseObjDataTier& p3 = p2.next[l3]; - if ((p3.lodLevel & lodLevelMask) == 0) - continue; - - for (int l4 = 0; l4 < static_cast( p3.next.size() ); l4++) + if (p3.type == ENG_TRIANGLE_TYPE_TRIANGLES) { - EngineBaseObjDataTier& p4 = p3.next[l4]; - - if (p4.type == ENG_TRIANGLE_TYPE_TRIANGLES) + for (int i = 0; i < static_cast( p3.vertices.size() ); i += 3) { - for (int i = 0; i < static_cast( p4.vertices.size() ); i += 3) - { - if (static_cast(actualCount) / total >= percent) - break; + if (static_cast(actualCount) / total >= percent) + break; - if (actualCount >= maxCount) - break; + if (actualCount >= maxCount) + break; - EngineTriangle t; - t.triangle[0] = p4.vertices[i]; - t.triangle[1] = p4.vertices[i+1]; - t.triangle[2] = p4.vertices[i+2]; - t.material = p4.material; - t.state = p4.state; - t.tex1Name = p2.tex1Name; - t.tex2Name = p2.tex2Name; + EngineTriangle t; + t.triangle[0] = p3.vertices[i]; + t.triangle[1] = p3.vertices[i+1]; + t.triangle[2] = p3.vertices[i+2]; + t.material = p3.material; + t.state = p3.state; + t.tex1Name = p2.tex1Name; + t.tex2Name = p2.tex2Name; - triangles.push_back(t); + triangles.push_back(t); - ++actualCount; - } + ++actualCount; } - else if (p4.type == ENG_TRIANGLE_TYPE_SURFACE) + } + else if (p3.type == ENG_TRIANGLE_TYPE_SURFACE) + { + for (int i = 0; i < static_cast( p3.vertices.size() ); i += 1) { - for (int i = 0; i < static_cast( p4.vertices.size() ); i += 1) - { - if (static_cast(actualCount) / total >= percent) - break; + if (static_cast(actualCount) / total >= percent) + break; - if (actualCount >= maxCount) - break; + if (actualCount >= maxCount) + break; - EngineTriangle t; - t.triangle[0] = p4.vertices[i]; - t.triangle[1] = p4.vertices[i+1]; - t.triangle[2] = p4.vertices[i+2]; - t.material = p4.material; - t.state = p4.state; - t.tex1Name = p2.tex1Name; - t.tex2Name = p2.tex2Name; + EngineTriangle t; + t.triangle[0] = p3.vertices[i]; + t.triangle[1] = p3.vertices[i+1]; + t.triangle[2] = p3.vertices[i+2]; + t.material = p3.material; + t.state = p3.state; + t.tex1Name = p2.tex1Name; + t.tex2Name = p2.tex2Name; - triangles.push_back(t); + triangles.push_back(t); - ++actualCount; - } + ++actualCount; } } } @@ -1201,12 +1146,12 @@ void CEngine::ChangeSecondTexture(int objRank, const std::string& tex2Name) void CEngine::ChangeTextureMapping(int objRank, const Material& mat, int state, const std::string& tex1Name, const std::string& tex2Name, - int lodLevelMask, EngineTextureMapping mode, + EngineTextureMapping mode, float au, float bu, float av, float bv) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); - EngineBaseObjDataTier* p4 = FindTriangles(objRank, mat, state, tex1Name, tex2Name, lodLevelMask); + EngineBaseObjDataTier* p4 = FindTriangles(objRank, mat, state, tex1Name, tex2Name); if (p4 == nullptr) return; @@ -1263,12 +1208,12 @@ void CEngine::ChangeTextureMapping(int objRank, const Material& mat, int state, void CEngine::TrackTextureMapping(int objRank, const Material& mat, int state, const std::string& tex1Name, const std::string& tex2Name, - int lodLevelMask, EngineTextureMapping mode, + EngineTextureMapping mode, float pos, float factor, float tl, float ts, float tt) { assert(objRank >= 0 && objRank < static_cast( m_objects.size() )); - EngineBaseObjDataTier* p4 = FindTriangles(objRank, mat, state, tex1Name, tex2Name, lodLevelMask); + EngineBaseObjDataTier* p4 = FindTriangles(objRank, mat, state, tex1Name, tex2Name); if (p4 == nullptr) return; @@ -1730,24 +1675,19 @@ void CEngine::UpdateGeometry() for (int l3 = 0; l3 < static_cast( p2.next.size() ); l3++) { - EngineBaseObjLODTier& p3 = p2.next[l3]; + EngineBaseObjDataTier& p3 = p2.next[l3]; - for (int l4 = 0; l4 < static_cast( p3.next.size() ); l4++) + for (int i = 0; i < static_cast( p3.vertices.size() ); i++) { - EngineBaseObjDataTier& p4 = p3.next[l4]; - - for (int i = 0; i < static_cast( p4.vertices.size() ); i++) - { - p1.bboxMin.x = Math::Min(p4.vertices[i].coord.x, p1.bboxMin.x); - p1.bboxMin.y = Math::Min(p4.vertices[i].coord.y, p1.bboxMin.y); - p1.bboxMin.z = Math::Min(p4.vertices[i].coord.z, p1.bboxMin.z); - p1.bboxMax.x = Math::Max(p4.vertices[i].coord.x, p1.bboxMax.x); - p1.bboxMax.y = Math::Max(p4.vertices[i].coord.y, p1.bboxMax.y); - p1.bboxMax.z = Math::Max(p4.vertices[i].coord.z, p1.bboxMax.z); - } - - p1.radius = Math::Max(p1.bboxMin.Length(), p1.bboxMax.Length()); + p1.bboxMin.x = Math::Min(p3.vertices[i].coord.x, p1.bboxMin.x); + p1.bboxMin.y = Math::Min(p3.vertices[i].coord.y, p1.bboxMin.y); + p1.bboxMin.z = Math::Min(p3.vertices[i].coord.z, p1.bboxMin.z); + p1.bboxMax.x = Math::Max(p3.vertices[i].coord.x, p1.bboxMax.x); + p1.bboxMax.y = Math::Max(p3.vertices[i].coord.y, p1.bboxMax.y); + p1.bboxMax.z = Math::Max(p3.vertices[i].coord.z, p1.bboxMax.z); } + + p1.radius = Math::Max(p1.bboxMin.Length(), p1.bboxMax.Length()); } } } @@ -1790,17 +1730,12 @@ void CEngine::UpdateStaticBuffers() for (int l3 = 0; l3 < static_cast( p2.next.size() ); l3++) { - EngineBaseObjLODTier& p3 = p2.next[l3]; + EngineBaseObjDataTier& p3 = p2.next[l3]; - for (int l4 = 0; l4 < static_cast( p3.next.size() ); l4++) - { - EngineBaseObjDataTier& p4 = p3.next[l4]; - - if (! p4.updateStaticBuffer) + if (! p3.updateStaticBuffer) continue; - UpdateStaticBuffer(p4); - } + UpdateStaticBuffer(p3); } } } @@ -1890,37 +1825,29 @@ int CEngine::DetectObject(Math::Point mouse) for (int l3 = 0; l3 < static_cast( p2.next.size() ); l3++) { - EngineBaseObjLODTier& p3 = p2.next[l3]; + EngineBaseObjDataTier& p3 = p2.next[l3]; - if (p3.lodLevel != LOD_Constant && p3.lodLevel != LOD_High) - continue; - - for (int l4 = 0; l4 < static_cast( p3.next.size() ); l4++) + if (p3.type == ENG_TRIANGLE_TYPE_TRIANGLES) { - EngineBaseObjDataTier& p4 = p3.next[l4]; - - if (p4.type == ENG_TRIANGLE_TYPE_TRIANGLES) + for (int i = 0; i < static_cast( p3.vertices.size() ); i += 3) { - for (int i = 0; i < static_cast( p4.vertices.size() ); i += 3) + float dist = 0.0f; + if (DetectTriangle(mouse, &p3.vertices[i], objRank, dist) && dist < min) { - float dist = 0.0f; - if (DetectTriangle(mouse, &p4.vertices[i], objRank, dist) && dist < min) - { - min = dist; - nearest = objRank; - } + min = dist; + nearest = objRank; } } - else if (p4.type == ENG_TRIANGLE_TYPE_SURFACE) + } + else if (p3.type == ENG_TRIANGLE_TYPE_SURFACE) + { + for (int i = 0; i < static_cast( p3.vertices.size() ) - 2; i += 1) { - for (int i = 0; i < static_cast( p4.vertices.size() ) - 2; i += 1) + float dist = 0.0f; + if (DetectTriangle(mouse, &p3.vertices[i], objRank, dist) && dist < min) { - float dist = 0.0f; - if (DetectTriangle(mouse, &p4.vertices[i], objRank, dist) && dist < min) - { - min = dist; - nearest = objRank; - } + min = dist; + nearest = objRank; } } } @@ -2005,43 +1932,6 @@ bool CEngine::IsVisible(int objRank) return false; } -bool CEngine::IsWithinLODLimit(float distance, LODLevel lodLevel) -{ - float min = 0.0f, max = 0.0f; - - if (lodLevel == LOD_Constant) - { - min = 0.0f; - max = m_terrainVision * m_clippingDistance; - } - else - { - if (lodLevel == LOD_High) - { - min = 0.0f; - max = 100.0f; - } - else if (lodLevel == LOD_Medium) - { - min = 100.0f; - max = 200.0f; - } - else if (lodLevel == LOD_Low) - { - min = 100.0f; - max = 1000000.0f; - } - - min *= m_size.x / 640.0f; - min *= 1.0f+m_objectDetail*2.0f; - - max *= m_size.x / 640.0f; - max *= 1.0f+m_objectDetail*2.0f; - } - - return distance >= min && distance < max; -} - bool CEngine::TransformPoint(Math::Vector& p2D, int objRank, Math::Vector p3D) { assert(objRank >= 0 && objRank < static_cast(m_objects.size())); @@ -3327,20 +3217,12 @@ void CEngine::Draw3DScene() for (int l3 = 0; l3 < static_cast( p2.next.size() ); l3++) { - EngineBaseObjLODTier& p3 = p2.next[l3]; + EngineBaseObjDataTier& p3 = p2.next[l3]; - if (! IsWithinLODLimit(m_objects[objRank].distance, p3.lodLevel)) - continue; + SetMaterial(p3.material); + SetState(p3.state); - for (int l4 = 0; l4 < static_cast( p3.next.size() ); l4++) - { - EngineBaseObjDataTier& p4 = p3.next[l4]; - - SetMaterial(p4.material); - SetState(p4.state); - - DrawObject(p4); - } + DrawObject(p3); } } } @@ -3398,26 +3280,18 @@ void CEngine::Draw3DScene() for (int l3 = 0; l3 < static_cast( p2.next.size() ); l3++) { - EngineBaseObjLODTier& p3 = p2.next[l3]; + EngineBaseObjDataTier& p3 = p2.next[l3]; - if (! IsWithinLODLimit(m_objects[objRank].distance, p3.lodLevel)) - continue; - - for (int l4 = 0; l4 < static_cast( p3.next.size() ); l4++) + if (m_objects[objRank].transparency != 0.0f) // transparent ? { - EngineBaseObjDataTier& p4 = p3.next[l4]; - - if (m_objects[objRank].transparency != 0.0f) // transparent ? - { - transparent = true; - continue; - } - - SetMaterial(p4.material); - SetState(p4.state); - - DrawObject(p4); + transparent = true; + continue; } + + SetMaterial(p3.material); + SetState(p3.state); + + DrawObject(p3); } } } @@ -3468,23 +3342,15 @@ void CEngine::Draw3DScene() for (int l3 = 0; l3 < static_cast( p2.next.size() ); l3++) { - EngineBaseObjLODTier& p3 = p2.next[l3]; + EngineBaseObjDataTier& p3 = p2.next[l3]; - if (! IsWithinLODLimit(m_objects[objRank].distance, p3.lodLevel)) + if (m_objects[objRank].transparency == 0.0f) continue; - for (int l4 = 0; l4 < static_cast( p3.next.size() ); l4++) - { - EngineBaseObjDataTier& p4 = p3.next[l4]; + SetMaterial(p3.material); + SetState(tState, tColor); - if (m_objects[objRank].transparency == 0.0f) - continue; - - SetMaterial(p4.material); - SetState(tState, tColor); - - DrawObject(p4); - } + DrawObject(p3); } } } @@ -3688,17 +3554,8 @@ void CEngine::RenderShadowMap() for (int l3 = 0; l3 < static_cast(p2.next.size()); l3++) { - EngineBaseObjLODTier& p3 = p2.next[l3]; - - if (!IsWithinLODLimit(m_objects[objRank].distance, p3.lodLevel)) - continue; - - for (int l4 = 0; l4 < static_cast(p3.next.size()); l4++) - { - EngineBaseObjDataTier& p4 = p3.next[l4]; - - DrawObject(p4); - } + EngineBaseObjDataTier& p3 = p2.next[l3]; + DrawObject(p3); } } } @@ -4046,20 +3903,12 @@ void CEngine::DrawInterface() for (int l3 = 0; l3 < static_cast( p2.next.size() ); l3++) { - EngineBaseObjLODTier& p3 = p2.next[l3]; + EngineBaseObjDataTier& p3 = p2.next[l3]; - if (! IsWithinLODLimit(m_objects[objRank].distance, p3.lodLevel)) - continue; + SetMaterial(p3.material); + SetState(p3.state); - for (int l4 = 0; l4 < static_cast( p3.next.size() ); l4++) - { - EngineBaseObjDataTier& p4 = p3.next[l4]; - - SetMaterial(p4.material); - SetState(p4.state); - - DrawObject(p4); - } + DrawObject(p3); } } } diff --git a/src/graphics/engine/engine.h b/src/graphics/engine/engine.h index f3684bcb..511e8eac 100644 --- a/src/graphics/engine/engine.h +++ b/src/graphics/engine/engine.h @@ -35,8 +35,6 @@ #include "graphics/core/texture.h" #include "graphics/core/vertex.h" -#include "graphics/engine/modelfile.h" - #include "math/intpoint.h" #include "math/matrix.h" #include "math/point.h" @@ -62,7 +60,7 @@ namespace Gfx { class CDevice; -class CModelManager; +class COldModelManager; class CLightManager; class CText; class CParticle; @@ -189,7 +187,7 @@ enum EngineObjectType /** * \struct EngineBaseObjDataTier - * \brief Tier 4 of object tree (data) + * \brief Tier 3 of object tree (data) */ struct EngineBaseObjDataTier { @@ -211,20 +209,6 @@ struct EngineBaseObjDataTier {} }; -/** - * \struct EngineBaseObjLODTier - * \brief Tier 3 of base object tree (LOD) - */ -struct EngineBaseObjLODTier -{ - LODLevel lodLevel; - std::vector next; - - inline EngineBaseObjLODTier(LODLevel _lodLevel = LOD_Constant) - : lodLevel(_lodLevel) - {} -}; - /** * \struct EngineBaseObjTexTier * \brief Tier 2 of base object tree (textures) @@ -235,7 +219,7 @@ struct EngineBaseObjTexTier Texture tex1; std::string tex2Name; Texture tex2; - std::vector next; + std::vector next; inline EngineBaseObjTexTier(const std::string& _tex1Name = "", const std::string& _tex2Name = "") : tex1Name(_tex1Name) @@ -261,7 +245,7 @@ struct EngineBaseObject Math::Vector bboxMax; //! Radius of the sphere at the origin float radius; - //! Next tier (LOD) + //! Next tier (Tex) std::vector next; inline EngineBaseObject() @@ -644,8 +628,7 @@ struct EngineMouse * The 4 tiers contain the following information: * - level 1 (EngineBaseObject) - geometric statistics * - level 2 (EngineBaseObjTexTier) - two textures (names and structs) applied to triangles, - * - level 3 (EngineBaseObjLODTier) - minumum and maximum LOD (=level of detail) - * - level 4 (EngineBaseObjDataTier) - type of object*, material, render state and the actual vertex data + * - level 3 (EngineBaseObjDataTier) - type of object*, material, render state and the actual vertex data * * *NOTE: type of object in this context means only the internal type in 3D engine. It is not related * to CObject types. @@ -697,7 +680,7 @@ public: //! Returns the text rendering engine CText* GetText(); - CModelManager* GetModelManager(); + COldModelManager* GetModelManager(); CPyroManager* GetPyroManager(); //! Returns the light manager CLightManager* GetLightManager(); @@ -809,12 +792,12 @@ public: EngineTriangleType triangleType, const Material& material, int state, std::string tex1Name, std::string tex2Name, - LODLevel lodLevel, bool globalUpdate); + bool globalUpdate); //! Adds a tier 4 engine object directly void AddBaseObjQuick(int baseObjRank, const EngineBaseObjDataTier& buffer, std::string tex1Name, std::string tex2Name, - LODLevel lodLevel, bool globalUpdate); + bool globalUpdate); // Objects @@ -862,11 +845,10 @@ public: //! Returns the first found tier 4 engine object for the given params or nullptr if not found EngineBaseObjDataTier* FindTriangles(int objRank, const Material& material, - int state, std::string tex1Name, std::string tex2Name, - int lodLevelMask); + int state, std::string tex1Name, std::string tex2Name); //! Returns a partial list of triangles for given object - int GetPartialTriangles(int objRank, int lodLevelMask, float percent, int maxCount, + int GetPartialTriangles(int objRank, float percent, int maxCount, std::vector& triangles); //! Changes the 2nd texure for given object @@ -875,13 +857,13 @@ public: //! Changes (recalculates) texture mapping for given object void ChangeTextureMapping(int objRank, const Material& mat, int state, const std::string& tex1Name, const std::string& tex2Name, - int lodLevelMask, EngineTextureMapping mode, + EngineTextureMapping mode, float au, float bu, float av, float bv); //! Changes texture mapping for robot tracks void TrackTextureMapping(int objRank, const Material& mat, int state, const std::string& tex1Name, const std::string& tex2Name, - int lodLevelMask, EngineTextureMapping mode, + EngineTextureMapping mode, float pos, float factor, float tl, float ts, float tt); //! Detects the target object that is selected with the mouse @@ -1297,10 +1279,8 @@ protected: //! Creates a new tier 2 object (texture) EngineBaseObjTexTier& AddLevel2(EngineBaseObject& p1, const std::string& tex1Name, const std::string& tex2Name); - //! Creates a new tier 3 object (LOD) - EngineBaseObjLODTier& AddLevel3(EngineBaseObjTexTier &p2, LODLevel lodLevel); - //! Creates a new tier 4 object (data) - EngineBaseObjDataTier& AddLevel4(EngineBaseObjLODTier &p3, EngineTriangleType type, + //! Creates a new tier 3 object (data) + EngineBaseObjDataTier& AddLevel3(EngineBaseObjTexTier &p3, EngineTriangleType type, const Material& mat, int state); //! Create texture and add it to cache @@ -1309,9 +1289,6 @@ protected: //! Tests whether the given object is visible bool IsVisible(int objRank); - //! Checks whether the given distance is within LOD min & max limit - bool IsWithinLODLimit(float distance, LODLevel lodLevel); - //! Detects whether an object is affected by the mouse bool DetectBBox(int objRank, Math::Point mouse); @@ -1341,7 +1318,7 @@ protected: CApplication* m_app; CSoundInterface* m_sound; CDevice* m_device; - std::unique_ptr m_modelManager; + std::unique_ptr m_modelManager; CText* m_text; CLightManager* m_lightMan; CParticle* m_particle; diff --git a/src/graphics/engine/modelfile.cpp b/src/graphics/engine/modelfile.cpp deleted file mode 100644 index c422f186..00000000 --- a/src/graphics/engine/modelfile.cpp +++ /dev/null @@ -1,1249 +0,0 @@ -/* - * This file is part of the Colobot: Gold Edition source code - * Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam - * http://epsiteс.ch; http://colobot.info; http://github.com/colobot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://gnu.org/licenses - */ - - -#include "graphics/engine/modelfile.h" - -#include "common/ioutils.h" -#include "common/logger.h" -#include "common/stringutils.h" - -#ifndef MODELFILE_NO_ENGINE -#include "common/resources/inputstream.h" -#endif - -#include "graphics/engine/engine.h" - -#include "math/geometry.h" - - -#include - -#include -#include - - -// Graphics module namespace -namespace Gfx { - - -bool ReadBinaryVertex(std::istream& stream, Vertex& vertex) -{ - vertex.coord.x = IOUtils::ReadBinaryFloat(stream); - vertex.coord.y = IOUtils::ReadBinaryFloat(stream); - vertex.coord.z = IOUtils::ReadBinaryFloat(stream); - vertex.normal.x = IOUtils::ReadBinaryFloat(stream); - vertex.normal.y = IOUtils::ReadBinaryFloat(stream); - vertex.normal.z = IOUtils::ReadBinaryFloat(stream); - vertex.texCoord.x = IOUtils::ReadBinaryFloat(stream); - vertex.texCoord.y = IOUtils::ReadBinaryFloat(stream); - - return !stream.fail(); -} - -bool WriteBinaryVertex(Vertex vertex, std::ostream& stream) -{ - IOUtils::WriteBinaryFloat(vertex.coord.x, stream); - IOUtils::WriteBinaryFloat(vertex.coord.y, stream); - IOUtils::WriteBinaryFloat(vertex.coord.z, stream); - IOUtils::WriteBinaryFloat(vertex.normal.x, stream); - IOUtils::WriteBinaryFloat(vertex.normal.y, stream); - IOUtils::WriteBinaryFloat(vertex.normal.z, stream); - IOUtils::WriteBinaryFloat(vertex.texCoord.x, stream); - IOUtils::WriteBinaryFloat(vertex.texCoord.y, stream); - - return !stream.fail(); -} - -bool ReadBinaryVertexTex2(std::istream& stream, VertexTex2& vertex) -{ - vertex.coord.x = IOUtils::ReadBinaryFloat(stream); - vertex.coord.y = IOUtils::ReadBinaryFloat(stream); - vertex.coord.z = IOUtils::ReadBinaryFloat(stream); - vertex.normal.x = IOUtils::ReadBinaryFloat(stream); - vertex.normal.y = IOUtils::ReadBinaryFloat(stream); - vertex.normal.z = IOUtils::ReadBinaryFloat(stream); - vertex.texCoord.x = IOUtils::ReadBinaryFloat(stream); - vertex.texCoord.y = IOUtils::ReadBinaryFloat(stream); - vertex.texCoord2.x = IOUtils::ReadBinaryFloat(stream); - vertex.texCoord2.y = IOUtils::ReadBinaryFloat(stream); - - return !stream.fail(); -} - -bool WriteBinaryVertexTex2(VertexTex2 vertex, std::ostream& stream) -{ - IOUtils::WriteBinaryFloat(vertex.coord.x, stream); - IOUtils::WriteBinaryFloat(vertex.coord.y, stream); - IOUtils::WriteBinaryFloat(vertex.coord.z, stream); - IOUtils::WriteBinaryFloat(vertex.normal.x, stream); - IOUtils::WriteBinaryFloat(vertex.normal.y, stream); - IOUtils::WriteBinaryFloat(vertex.normal.z, stream); - IOUtils::WriteBinaryFloat(vertex.texCoord.x, stream); - IOUtils::WriteBinaryFloat(vertex.texCoord.y, stream); - IOUtils::WriteBinaryFloat(vertex.texCoord2.x, stream); - IOUtils::WriteBinaryFloat(vertex.texCoord2.y, stream); - - return !stream.fail(); -} - -bool ReadTextVertexTex2(const std::string& text, VertexTex2& vertex) -{ - std::stringstream stream; - stream.str(text); - - std::string what; - - stream >> what; - if (what != "c") - return false; - - stream >> vertex.coord.x >> vertex.coord.y >> vertex.coord.z; - - stream >> what; - if (what != "n") - return false; - - stream >> vertex.normal.x >> vertex.normal.y >> vertex.normal.z; - - stream >> what; - if (what != "t1") - return false; - - stream >> vertex.texCoord.x >> vertex.texCoord.y; - - stream >> what; - if (what != "t2") - return false; - - stream >> vertex.texCoord2.x >> vertex.texCoord2.y; - - return !stream.fail(); -} - -bool WriteTextVertexTex2(const VertexTex2& vertex, std::ostream& stream) -{ - stream << "c " << vertex.coord.x << " " << vertex.coord.y << " " << vertex.coord.z; - stream << " n " << vertex.normal.x << " " << vertex.normal.y << " " << vertex.normal.z; - stream << " t1 " << vertex.texCoord.x << " " << vertex.texCoord.y; - stream << " t2 " << vertex.texCoord2.x << " " << vertex.texCoord2.y; - stream << std::endl; - - return !stream.fail(); -} - -bool ReadBinaryMaterial(std::istream& stream, Material& material) -{ - material.diffuse.r = IOUtils::ReadBinaryFloat(stream); - material.diffuse.g = IOUtils::ReadBinaryFloat(stream); - material.diffuse.b = IOUtils::ReadBinaryFloat(stream); - material.diffuse.a = IOUtils::ReadBinaryFloat(stream); - - material.ambient.r = IOUtils::ReadBinaryFloat(stream); - material.ambient.g = IOUtils::ReadBinaryFloat(stream); - material.ambient.b = IOUtils::ReadBinaryFloat(stream); - material.ambient.a = IOUtils::ReadBinaryFloat(stream); - - material.specular.r = IOUtils::ReadBinaryFloat(stream); - material.specular.g = IOUtils::ReadBinaryFloat(stream); - material.specular.b = IOUtils::ReadBinaryFloat(stream); - material.specular.a = IOUtils::ReadBinaryFloat(stream); - - /* emissive.r = */ IOUtils::ReadBinaryFloat(stream); - /* emissive.g = */ IOUtils::ReadBinaryFloat(stream); - /* emissive.b = */ IOUtils::ReadBinaryFloat(stream); - /* emissive.a = */ IOUtils::ReadBinaryFloat(stream); - - /* power = */ IOUtils::ReadBinaryFloat(stream); - - return !stream.fail(); -} - -bool WriteBinaryMaterial(const Material& material, std::ostream& stream) -{ - IOUtils::WriteBinaryFloat(material.diffuse.r, stream); - IOUtils::WriteBinaryFloat(material.diffuse.g, stream); - IOUtils::WriteBinaryFloat(material.diffuse.b, stream); - IOUtils::WriteBinaryFloat(material.diffuse.a, stream); - - IOUtils::WriteBinaryFloat(material.ambient.r, stream); - IOUtils::WriteBinaryFloat(material.ambient.g, stream); - IOUtils::WriteBinaryFloat(material.ambient.b, stream); - IOUtils::WriteBinaryFloat(material.ambient.a, stream); - - IOUtils::WriteBinaryFloat(material.specular.r, stream); - IOUtils::WriteBinaryFloat(material.specular.g, stream); - IOUtils::WriteBinaryFloat(material.specular.b, stream); - IOUtils::WriteBinaryFloat(material.specular.a, stream); - - /* emissive.r */ IOUtils::WriteBinaryFloat(0.0f, stream); - /* emissive.g */ IOUtils::WriteBinaryFloat(0.0f, stream); - /* emissive.b */ IOUtils::WriteBinaryFloat(0.0f, stream); - /* emissive.a */ IOUtils::WriteBinaryFloat(0.0f, stream); - - /* power */ IOUtils::WriteBinaryFloat(0.0f, stream); - - return !stream.fail(); -} - -bool ReadTextMaterial(const std::string& text, Material& material) -{ - std::stringstream stream; - stream.str(text); - - std::string what; - - stream >> what; - if (what != "dif") - return false; - - stream >> material.diffuse.r - >> material.diffuse.g - >> material.diffuse.b - >> material.diffuse.a; - - stream >> what; - if (what != "amb") - return false; - - stream >> material.ambient.r - >> material.ambient.g - >> material.ambient.b - >> material.ambient.a; - - stream >> what; - if (what != "spc") - return false; - - stream >> material.specular.r - >> material.specular.g - >> material.specular.b - >> material.specular.a; - - return !stream.fail(); -} - -bool WriteTextMaterial(const Material& material, std::ostream& stream) -{ - stream << "dif " << material.diffuse.r - << " " << material.diffuse.g - << " " << material.diffuse.b - << " " << material.diffuse.a; - - stream << " amb " << material.ambient.r - << " " << material.ambient.g - << " " << material.ambient.b - << " " << material.ambient.a; - - stream << " spc " << material.specular.r - << " " << material.specular.g << " " - << material.specular.b << " " - << material.specular.a; - - stream << std::endl; - - return !stream.fail(); -} - -template -bool ReadLineValue(std::istream& stream, const std::string& prefix, T& value) -{ - std::string line; - while (true) - { - if (stream.eof() || stream.fail()) - return false; - - std::getline(stream, line); - if (!line.empty() && line[0] != '#') - break; - } - - std::stringstream s; - s.str(line); - - std::string what; - s >> what; - if (what != prefix) - return false; - - s >> value; - - return true; -} - -bool ReadLineString(std::istream& stream, const std::string& prefix, std::string& value) -{ - std::string line; - while (true) - { - if (stream.eof() || stream.fail()) - return false; - - std::getline(stream, line); - if (!line.empty() && line[0] != '#') - break; - } - - std::stringstream s; - s.str(line); - - std::string what; - s >> what; - if (what != prefix) - return false; - - std::getline(s, value); - - return true; -} - - -CModelFile::CModelFile() - : m_printDebugInfo(false) -{ -} - -CModelFile::~CModelFile() -{ -} - - -/******************************************************* - Deprecated formats - *******************************************************/ - -/** - * \struct OldModelHeader - * \brief Colobot binary model header info - * - * @deprecated - */ -struct OldModelHeader -{ - //! Revision number - int revision; - //! Version number - int version; - //! Total number of triangles - int totalTriangles; - //! Reserved area - int reserved[10]; - - OldModelHeader() - { - memset(this, 0, sizeof(*this)); - } -}; - - -/** - * \struct OldModelTriangle1 - * \brief Colobot binary model file version 1 - * - * @deprecated - */ -struct OldModelTriangle1 -{ - char used; - char selected; - Vertex p1; - Vertex p2; - Vertex p3; - Material material; - char texName[20]; - float min; - float max; - - OldModelTriangle1() - { - memset(this, 0, sizeof(*this)); - } -}; - -/** - * \struct OldModelTriangle2 - * \brief Colobot binary model file version 2 - * - * @deprecated - */ -struct OldModelTriangle2 -{ - char used; - char selected; - Vertex p1; - Vertex p2; - Vertex p3; - Material material; - char texName[20]; - float min; - float max; - long state; - short reserved1; - short reserved2; - short reserved3; - short reserved4; - OldModelTriangle2() - { - memset(this, 0, sizeof(*this)); - } -}; - -/** - * \struct OldModelTriangle3 - * \brief Colobot binary model file version 3 - * - * @deprecated - */ -struct OldModelTriangle3 -{ - char used; - char selected; - VertexTex2 p1; - VertexTex2 p2; - VertexTex2 p3; - Material material; - char texName[20]; - float min; - float max; - long state; - short texNum2; - short reserved2; - short reserved3; - short reserved4; - - OldModelTriangle3() - { - memset(this, 0, sizeof(*this)); - } -}; - -bool CModelFile::ReadModel(const std::string& fileName) -{ - m_triangles.clear(); - - #ifndef MODELFILE_NO_ENGINE - CInputStream stream; - stream.open(fileName); - if (!stream.is_open()) - { - GetLogger()->Error("Could not open file '%s'\n", fileName.c_str()); - return false; - } - #else - std::ifstream stream; - stream.open(fileName); - if (!stream.good()) - { - GetLogger()->Error("Could not open file '%s'\n", fileName.c_str()); - return false; - } - #endif - - return ReadModel(stream); -} - -bool CModelFile::ReadModel(std::istream& stream) -{ - m_triangles.clear(); - - OldModelHeader header; - - header.revision = IOUtils::ReadBinary<4, int>(stream); - header.version = IOUtils::ReadBinary<4, int>(stream); - header.totalTriangles = IOUtils::ReadBinary<4, int>(stream); - for (int i = 0; i < 10; ++i) - header.reserved[i] = IOUtils::ReadBinary<4, int>(stream); - - - if (!stream.good()) - { - GetLogger()->Error("Error reading model file header\n"); - return false; - } - - // Old model version #1 - if ( (header.revision == 1) && (header.version == 0) ) - { - for (int i = 0; i < header.totalTriangles; ++i) - { - OldModelTriangle1 t; - t.used = IOUtils::ReadBinary<1, char>(stream); - t.selected = IOUtils::ReadBinary<1, char>(stream); - - /* padding */ IOUtils::ReadBinary<2, unsigned int>(stream); - - ReadBinaryVertex(stream, t.p1); - ReadBinaryVertex(stream, t.p2); - ReadBinaryVertex(stream, t.p3); - - ReadBinaryMaterial(stream, t.material); - stream.read(t.texName, 20); - t.min = IOUtils::ReadBinaryFloat(stream); - t.max = IOUtils::ReadBinaryFloat(stream); - - if (stream.fail()) - { - GetLogger()->Error("Error reading model data\n"); - return false; - } - - ModelTriangle triangle; - triangle.p1.FromVertex(t.p1); - triangle.p2.FromVertex(t.p2); - triangle.p3.FromVertex(t.p3); - - triangle.material = t.material; - triangle.tex1Name = std::string(t.texName); - triangle.lodLevel = MinMaxToLodLevel(t.min, t.max); - - m_triangles.push_back(triangle); - } - } - else if ( header.revision == 1 && header.version == 1 ) - { - for (int i = 0; i < header.totalTriangles; ++i) - { - OldModelTriangle2 t; - t.used = IOUtils::ReadBinary<1, char>(stream); - t.selected = IOUtils::ReadBinary<1, char>(stream); - - /* padding */ IOUtils::ReadBinary<2, unsigned int>(stream); - - ReadBinaryVertex(stream, t.p1); - ReadBinaryVertex(stream, t.p2); - ReadBinaryVertex(stream, t.p3); - - ReadBinaryMaterial(stream, t.material); - stream.read(t.texName, 20); - t.min = IOUtils::ReadBinaryFloat(stream); - t.max = IOUtils::ReadBinaryFloat(stream); - t.state = IOUtils::ReadBinary<4, long>(stream); - - t.reserved1 = IOUtils::ReadBinary<2, short>(stream); - t.reserved2 = IOUtils::ReadBinary<2, short>(stream); - t.reserved3 = IOUtils::ReadBinary<2, short>(stream); - t.reserved4 = IOUtils::ReadBinary<2, short>(stream); - - if (stream.fail()) - { - GetLogger()->Error("Error reading model data\n"); - return false; - } - - ModelTriangle triangle; - triangle.p1.FromVertex(t.p1); - triangle.p2.FromVertex(t.p2); - triangle.p3.FromVertex(t.p3); - - triangle.material = t.material; - triangle.tex1Name = std::string(t.texName); - triangle.lodLevel = MinMaxToLodLevel(t.min, t.max); - triangle.state = t.state; - - m_triangles.push_back(triangle); - } - } - else - { - for (int i = 0; i < header.totalTriangles; ++i) - { - OldModelTriangle3 t; - t.used = IOUtils::ReadBinary<1, char>(stream); - t.selected = IOUtils::ReadBinary<1, char>(stream); - - /* padding */ IOUtils::ReadBinary<2, unsigned int>(stream); - - ReadBinaryVertexTex2(stream, t.p1); - ReadBinaryVertexTex2(stream, t.p2); - ReadBinaryVertexTex2(stream, t.p3); - - ReadBinaryMaterial(stream, t.material); - stream.read(t.texName, 20); - t.min = IOUtils::ReadBinaryFloat(stream); - t.max = IOUtils::ReadBinaryFloat(stream); - t.state = IOUtils::ReadBinary<4, long>(stream); - t.texNum2 = IOUtils::ReadBinary<2, short>(stream); - - t.reserved2 = IOUtils::ReadBinary<2, short>(stream); - t.reserved3 = IOUtils::ReadBinary<2, short>(stream); - t.reserved4 = IOUtils::ReadBinary<2, short>(stream); - - if (stream.fail()) - { - GetLogger()->Error("Error reading model data\n"); - return false; - } - - ModelTriangle triangle; - triangle.p1 = t.p1; - triangle.p2 = t.p2; - triangle.p3 = t.p3; - - triangle.material = t.material; - triangle.tex1Name = std::string(t.texName); - triangle.lodLevel = MinMaxToLodLevel(t.min, t.max); - triangle.state = t.state; - triangle.variableTex2 = t.texNum2 == 1; - - if (!triangle.variableTex2 && t.texNum2 != 0) - { - if (t.texNum2 >= 1 && t.texNum2 <= 10) - triangle.state |= ENG_RSTATE_DUAL_BLACK; - - if (t.texNum2 >= 11 && t.texNum2 <= 20) - triangle.state |= ENG_RSTATE_DUAL_WHITE; - - char tex2Name[20] = { 0 }; - sprintf(tex2Name, "dirty%.2d.png", t.texNum2); // hardcoded as in original code - triangle.tex2Name = tex2Name; - } - - m_triangles.push_back(triangle); - } - } - - for (int i = 0; i < static_cast( m_triangles.size() ); ++i) - { - // All extensions are now png - m_triangles[i].tex1Name = StrUtils::Replace(m_triangles[i].tex1Name, "bmp", "png"); - m_triangles[i].tex1Name = StrUtils::Replace(m_triangles[i].tex1Name, "tga", "png"); - - m_triangles[i].tex2Name = StrUtils::Replace(m_triangles[i].tex2Name, "bmp", "png"); - m_triangles[i].tex2Name = StrUtils::Replace(m_triangles[i].tex2Name, "tga", "png"); - - // TODO: fix this in model files - if (m_triangles[i].tex1Name == "plant.png") - m_triangles[i].state |= ENG_RSTATE_ALPHA; - - if (m_printDebugInfo) - { - GetLogger()->Trace("ModelTriangle %d\n", i+1); - std::string s1 = m_triangles[i].p1.ToString(); - GetLogger()->Trace(" p1: %s\n", s1.c_str()); - std::string s2 = m_triangles[i].p2.ToString(); - GetLogger()->Trace(" p2: %s\n", s2.c_str()); - std::string s3 = m_triangles[i].p3.ToString(); - GetLogger()->Trace(" p3: %s\n", s3.c_str()); - - std::string d = m_triangles[i].material.diffuse.ToString(); - std::string a = m_triangles[i].material.ambient.ToString(); - std::string s = m_triangles[i].material.specular.ToString(); - GetLogger()->Trace(" mat: d: %s a: %s s: %s\n", d.c_str(), a.c_str(), s.c_str()); - - GetLogger()->Trace(" tex1: %s tex2: %s\n", m_triangles[i].tex1Name.c_str(), - m_triangles[i].variableTex2 ? "(variable)" : m_triangles[i].tex2Name.c_str()); - GetLogger()->Trace(" lod level: %d\n", m_triangles[i].lodLevel); - GetLogger()->Trace(" state: %ld\n", m_triangles[i].state); - } - } - - return true; -} - -bool CModelFile::WriteModel(const std::string& fileName) -{ - std::ofstream stream; - stream.open(fileName.c_str(), std::ios_base::out | std::ios_base::binary); - if (!stream.good()) - { - GetLogger()->Error("Could not open file '%s'\n", fileName.c_str()); - return false; - } - - return WriteModel(stream); -} - -bool CModelFile::WriteModel(std::ostream& stream) -{ - if (m_triangles.size() == 0) - { - GetLogger()->Error("Empty model\n"); - return false; - } - - OldModelHeader header; - header.revision = 1; - header.version = 2; - header.totalTriangles = m_triangles.size(); - - IOUtils::WriteBinary<4, int>(header.revision, stream); - IOUtils::WriteBinary<4, int>(header.version, stream); - IOUtils::WriteBinary<4, int>(header.totalTriangles, stream); - for (int i = 0; i < 10; ++i) - IOUtils::WriteBinary<4, int>(header.reserved[i], stream); - - for (int i = 0; i < static_cast( m_triangles.size() ); ++i) - { - OldModelTriangle3 t; - - t.used = true; - - t.p1 = m_triangles[i].p1; - t.p2 = m_triangles[i].p2; - t.p3 = m_triangles[i].p3; - - t.material = m_triangles[i].material; - strncpy(t.texName, m_triangles[i].tex1Name.c_str(), 20); - LODLevelToMinMax(m_triangles[i].lodLevel, t.min, t.max); - t.state = m_triangles[i].state; - - int no = 0; - if (m_triangles[i].variableTex2) - no = 1; - else - sscanf(m_triangles[i].tex2Name.c_str(), "dirty%d.png", &no); // hardcoded as in the original code - - t.texNum2 = no; - - - IOUtils::WriteBinary<1, char>(t.used, stream); - IOUtils::WriteBinary<1, char>(t.selected, stream); - - /* padding */ IOUtils::WriteBinary<2, unsigned int>(0, stream); - - WriteBinaryVertexTex2(t.p1, stream); - WriteBinaryVertexTex2(t.p2, stream); - WriteBinaryVertexTex2(t.p3, stream); - - WriteBinaryMaterial(t.material, stream); - stream.write(t.texName, 20); - IOUtils::WriteBinaryFloat(t.min, stream); - IOUtils::WriteBinaryFloat(t.max, stream); - IOUtils::WriteBinary<4, long>(t.state, stream); - IOUtils::WriteBinary<2, short>(t.texNum2, stream); - - IOUtils::WriteBinary<2, short>(t.reserved2, stream); - IOUtils::WriteBinary<2, short>(t.reserved3, stream); - IOUtils::WriteBinary<2, short>(t.reserved4, stream); - } - - return true; -} - -LODLevel CModelFile::MinMaxToLodLevel(float min, float max) -{ - if (min == 0.0f && max == 100.0f) - return LOD_High; - else if (min == 100.0f && max == 200.0f) - return LOD_Medium; - else if (min == 200.0f && max == 1000000.0f) - return LOD_Low; - else if (min == 0.0f && max == 1000000.0f) - return LOD_Constant; - - return LOD_Constant; -} - -void CModelFile::LODLevelToMinMax(LODLevel lodLevel, float& min, float& max) -{ - switch (lodLevel) - { - case LOD_High: - min = 0.0f; - max = 100.0f; - break; - - case LOD_Medium: - min = 100.0f; - max = 200.0f; - break; - - case LOD_Low: - min = 200.0f; - max = 1000000.0f; - break; - - case LOD_Constant: - min = 0.0f; - max = 1000000.0f; - break; - } -} - - -/******************************************************* - New formats - *******************************************************/ - -/** - * \struct NewModelHeader - * \brief Header for new binary model file - */ -struct NewModelHeader -{ - //! File version (1, 2, ...) - int version; - //! Total number of triangles - int totalTriangles; - - NewModelHeader() - { - version = 0; - totalTriangles = 0; - } -}; - -/** - * \struct NewModelTriangle1 - * \brief Triangle of new binary model file - * - * NOTE: at this time, it is identical to ModelTriangle struct, but it may change - * independently in the future. - */ -struct NewModelTriangle1 -{ - //! 1st vertex - VertexTex2 p1; - //! 2nd vertex - VertexTex2 p2; - //! 3rd vertex - VertexTex2 p3; - //! Material - Material material; - //! Name of 1st texture - std::string tex1Name; - //! Name of 2nd texture - std::string tex2Name; - //! If true, 2nd texture will be taken from current engine setting - bool variableTex2; - //! LOD level - int lodLevel; - //! Rendering state to be set - int state; - - NewModelTriangle1() - { - variableTex2 = true; - lodLevel = 0; - state = 0; - } -}; - - -bool CModelFile::ReadTextModel(const std::string& fileName) -{ - #ifndef MODELFILE_NO_ENGINE - CInputStream stream; - stream.open(fileName); - if (!stream.is_open()) - { - GetLogger()->Error("Could not open file '%s'\n", fileName.c_str()); - return false; - } - #else - std::ifstream stream; - stream.open(fileName); - if (!stream.good()) - { - GetLogger()->Error("Could not open file '%s'\n", fileName.c_str()); - return false; - } - #endif - - return ReadTextModel(stream); -} - -bool CModelFile::ReadTextModel(std::istream& stream) -{ - m_triangles.clear(); - - NewModelHeader header; - - bool headOk = ReadLineValue(stream, "version", header.version) && - ReadLineValue(stream, "total_triangles", header.totalTriangles); - - if (!headOk || !stream.good()) - { - GetLogger()->Error("Error reading model file header\n"); - return false; - } - - // New model version 1 - if (header.version == 1) - { - for (int i = 0; i < header.totalTriangles; ++i) - { - NewModelTriangle1 t; - - std::string p1Text, p2Text, p3Text; - std::string matText; - char varTex2Ch = 0; - - bool triOk = ReadLineString(stream, "p1", p1Text) && - ReadTextVertexTex2(p1Text, t.p1) && - ReadLineString(stream, "p2", p2Text) && - ReadTextVertexTex2(p2Text, t.p2) && - ReadLineString(stream, "p3", p3Text) && - ReadTextVertexTex2(p3Text, t.p3) && - ReadLineString(stream, "mat", matText) && - ReadTextMaterial(matText, t.material) && - ReadLineValue(stream, "tex1", t.tex1Name) && - ReadLineValue(stream, "tex2", t.tex2Name) && - ReadLineValue(stream, "var_tex2", varTex2Ch) && - ReadLineValue(stream, "lod_level", t.lodLevel) && - ReadLineValue(stream, "state", t.state); - - if (!triOk || stream.fail()) - { - GetLogger()->Error("Error reading model data\n"); - return false; - } - - t.variableTex2 = varTex2Ch == 'Y'; - - - ModelTriangle triangle; - triangle.p1 = t.p1; - triangle.p2 = t.p2; - triangle.p3 = t.p3; - triangle.material = t.material; - triangle.tex1Name = t.tex1Name; - triangle.tex2Name = t.tex2Name; - triangle.variableTex2 = t.variableTex2; - triangle.state = t.state; - - switch (t.lodLevel) - { - case 0: triangle.lodLevel = LOD_Constant; break; - case 1: triangle.lodLevel = LOD_Low; break; - case 2: triangle.lodLevel = LOD_Medium; break; - case 3: triangle.lodLevel = LOD_High; break; - default: break; - } - - m_triangles.push_back(triangle); - - continue; - } - } - else - { - GetLogger()->Error("Unknown model file version\n"); - return false; - } - - for (int i = 0; i < static_cast( m_triangles.size() ); ++i) - { - GetLogger()->Trace("ModelTriangle %d\n", i+1); - std::string s1 = m_triangles[i].p1.ToString(); - GetLogger()->Trace(" p1: %s\n", s1.c_str()); - std::string s2 = m_triangles[i].p2.ToString(); - GetLogger()->Trace(" p2: %s\n", s2.c_str()); - std::string s3 = m_triangles[i].p3.ToString(); - GetLogger()->Trace(" p3: %s\n", s3.c_str()); - - std::string d = m_triangles[i].material.diffuse.ToString(); - std::string a = m_triangles[i].material.ambient.ToString(); - std::string s = m_triangles[i].material.specular.ToString(); - GetLogger()->Trace(" mat: d: %s a: %s s: %s\n", d.c_str(), a.c_str(), s.c_str()); - - GetLogger()->Trace(" tex1: %s tex2: %s\n", m_triangles[i].tex1Name.c_str(), m_triangles[i].tex2Name.c_str()); - GetLogger()->Trace(" lod level: %d\n", m_triangles[i].lodLevel); - GetLogger()->Trace(" state: %ld\n", m_triangles[i].state); - } - - return true; -} - -bool CModelFile::WriteTextModel(const std::string &fileName) -{ - std::ofstream stream; - stream.open(fileName.c_str(), std::ios_base::out); - if (!stream.good()) - { - GetLogger()->Error("Could not open file '%s'\n", fileName.c_str()); - return false; - } - - return WriteTextModel(stream); -} - -bool CModelFile::WriteTextModel(std::ostream& stream) -{ - if (m_triangles.size() == 0) - { - GetLogger()->Error("Empty model\n"); - return false; - } - - NewModelHeader header; - - header.version = 1; - header.totalTriangles = m_triangles.size(); - - stream << "# Colobot text model" << std::endl; - stream << std::endl; - stream << "### HEAD" << std::endl; - stream << "version " << header.version << std::endl; - stream << "total_triangles " << header.totalTriangles << std::endl; - stream << std::endl; - stream << "### TRIANGLES" << std::endl; - - for (int i = 0; i < static_cast( m_triangles.size() ); ++i) - { - NewModelTriangle1 t; - - t.p1 = m_triangles[i].p1; - t.p2 = m_triangles[i].p2; - t.p3 = m_triangles[i].p3; - t.material = m_triangles[i].material; - t.tex1Name = m_triangles[i].tex1Name; - t.tex2Name = m_triangles[i].tex2Name; - t.variableTex2 = m_triangles[i].variableTex2; - t.state = m_triangles[i].state; - - switch (m_triangles[i].lodLevel) - { - case LOD_Constant: t.lodLevel = 0; break; - case LOD_Low: t.lodLevel = 1; break; - case LOD_Medium: t.lodLevel = 2; break; - case LOD_High: t.lodLevel = 3; break; - } - - stream << "p1 "; - WriteTextVertexTex2(t.p1, stream); - stream << "p2 "; - WriteTextVertexTex2(t.p2, stream); - stream << "p3 "; - WriteTextVertexTex2(t.p3, stream); - stream << "mat "; - WriteTextMaterial(t.material, stream); - - stream << "tex1 " << t.tex1Name << std::endl; - stream << "tex2 " << t.tex2Name << std::endl; - stream << "var_tex2 " << (t.variableTex2 ? 'Y' : 'N') << std::endl; - stream << "lod_level " << t.lodLevel << std::endl; - stream << "state " << t.state << std::endl; - - stream << std::endl; - - if (stream.fail()) - { - GetLogger()->Error("Error writing model file\n"); - return false; - } - } - - return true; -} - -bool CModelFile::ReadBinaryModel(const std::string& fileName) -{ - #ifndef MODELFILE_NO_ENGINE - CInputStream stream; - stream.open(fileName); - if (!stream.is_open()) - { - GetLogger()->Error("Could not open file '%s'\n", fileName.c_str()); - return false; - } - #else - std::ifstream stream; - stream.open(fileName); - if (!stream.good()) - { - GetLogger()->Error("Could not open file '%s'\n", fileName.c_str()); - return false; - } - #endif - - return ReadBinaryModel(stream); -} - -bool CModelFile::ReadBinaryModel(std::istream& stream) -{ - m_triangles.clear(); - - NewModelHeader header; - - header.version = IOUtils::ReadBinary<4, int>(stream); - header.totalTriangles = IOUtils::ReadBinary<4, int>(stream); - - if (!stream.good()) - { - GetLogger()->Error("Error reading model file header\n"); - return false; - } - - // New model version 1 - if (header.version == 1) - { - for (int i = 0; i < header.totalTriangles; ++i) - { - NewModelTriangle1 t; - - ReadBinaryVertexTex2(stream, t.p1); - ReadBinaryVertexTex2(stream, t.p2); - ReadBinaryVertexTex2(stream, t.p3); - ReadBinaryMaterial(stream, t.material); - t.tex1Name = IOUtils::ReadBinaryString<1>(stream); - t.tex2Name = IOUtils::ReadBinaryString<1>(stream); - t.variableTex2 = IOUtils::ReadBinaryBool(stream); - t.lodLevel = IOUtils::ReadBinary<4, int>(stream); - t.state = IOUtils::ReadBinary<4, int>(stream); - - if (stream.fail()) - { - GetLogger()->Error("Error reading model data\n"); - return false; - } - - ModelTriangle triangle; - triangle.p1 = t.p1; - triangle.p2 = t.p2; - triangle.p3 = t.p3; - triangle.material = t.material; - triangle.tex1Name = t.tex1Name; - triangle.tex2Name = t.tex2Name; - triangle.variableTex2 = t.variableTex2; - triangle.state = t.state; - - switch (t.lodLevel) - { - case 0: triangle.lodLevel = LOD_Constant; break; - case 1: triangle.lodLevel = LOD_Low; break; - case 2: triangle.lodLevel = LOD_Medium; break; - case 3: triangle.lodLevel = LOD_High; break; - default: break; - } - - m_triangles.push_back(triangle); - } - } - else - { - GetLogger()->Error("Unknown model file version\n"); - return false; - } - - if (m_printDebugInfo) - { - for (int i = 0; i < static_cast( m_triangles.size() ); ++i) - { - GetLogger()->Trace("ModelTriangle %d\n", i+1); - std::string s1 = m_triangles[i].p1.ToString(); - GetLogger()->Trace(" p1: %s\n", s1.c_str()); - std::string s2 = m_triangles[i].p2.ToString(); - GetLogger()->Trace(" p2: %s\n", s2.c_str()); - std::string s3 = m_triangles[i].p3.ToString(); - GetLogger()->Trace(" p3: %s\n", s3.c_str()); - - std::string d = m_triangles[i].material.diffuse.ToString(); - std::string a = m_triangles[i].material.ambient.ToString(); - std::string s = m_triangles[i].material.specular.ToString(); - GetLogger()->Trace(" mat: d: %s a: %s s: %s\n", d.c_str(), a.c_str(), s.c_str()); - - GetLogger()->Trace(" tex1: %s tex2: %s\n", m_triangles[i].tex1Name.c_str(), m_triangles[i].tex2Name.c_str()); - GetLogger()->Trace(" lod level: %d\n", m_triangles[i].lodLevel); - GetLogger()->Trace(" state: %ld\n", m_triangles[i].state); - } - } - - return true; -} - -bool CModelFile::WriteBinaryModel(const std::string& fileName) -{ - std::ofstream stream; - stream.open(fileName.c_str(), std::ios_base::out | std::ios_base::binary); - if (!stream.good()) - { - GetLogger()->Error("Could not open file '%s'\n", fileName.c_str()); - return false; - } - - return WriteBinaryModel(stream); -} - -bool CModelFile::WriteBinaryModel(std::ostream& stream) -{ - if (m_triangles.size() == 0) - { - GetLogger()->Error("Empty model\n"); - return false; - } - - NewModelHeader header; - - header.version = 1; - header.totalTriangles = m_triangles.size(); - - IOUtils::WriteBinary<4, int>(header.version, stream); - IOUtils::WriteBinary<4, int>(header.totalTriangles, stream); - - for (int i = 0; i < static_cast( m_triangles.size() ); ++i) - { - NewModelTriangle1 t; - - t.p1 = m_triangles[i].p1; - t.p2 = m_triangles[i].p2; - t.p3 = m_triangles[i].p3; - t.material = m_triangles[i].material; - t.tex1Name = m_triangles[i].tex1Name; - t.tex2Name = m_triangles[i].tex2Name; - t.variableTex2 = m_triangles[i].variableTex2; - t.state = m_triangles[i].state; - - switch (m_triangles[i].lodLevel) - { - case LOD_Constant: t.lodLevel = 0; break; - case LOD_Low: t.lodLevel = 1; break; - case LOD_Medium: t.lodLevel = 2; break; - case LOD_High: t.lodLevel = 3; break; - } - - WriteBinaryVertexTex2(t.p1, stream); - WriteBinaryVertexTex2(t.p2, stream); - WriteBinaryVertexTex2(t.p3, stream); - WriteBinaryMaterial(t.material, stream); - IOUtils::WriteBinaryString<1>(t.tex1Name, stream); - IOUtils::WriteBinaryString<1>(t.tex2Name, stream); - IOUtils::WriteBinaryBool(t.variableTex2, stream); - IOUtils::WriteBinary<4, int>(t.lodLevel, stream); - IOUtils::WriteBinary<4, int>(t.state, stream); - - if (stream.fail()) - { - GetLogger()->Error("Error writing model file\n"); - return false; - } - } - - return true; -} - - -const std::vector& CModelFile::GetTriangles() -{ - return m_triangles; -} - -int CModelFile::GetTriangleCount() -{ - return m_triangles.size(); -} - -void CModelFile::SetPrintDebugInfo(bool printDebugInfo) -{ - m_printDebugInfo = printDebugInfo; -} - -} // namespace Gfx - diff --git a/src/graphics/engine/modelfile.h b/src/graphics/engine/modelfile.h deleted file mode 100644 index f6ac2290..00000000 --- a/src/graphics/engine/modelfile.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - * This file is part of the Colobot: Gold Edition source code - * Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam - * http://epsiteс.ch; http://colobot.info; http://github.com/colobot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://gnu.org/licenses - */ - -/** - * \file graphics/engine/modelfile.h - * \brief Model loading - CModelFile class (aka modfile) - */ - -#pragma once - - -#include "graphics/core/vertex.h" -#include "graphics/core/material.h" - -#include "math/vector.h" - -#include -#include -#include - - - -// Graphics module namespace -namespace Gfx { - - -/** - * \enum LODLevel - * \brief Level-of-detail - * - * A quantified replacement for older values of min/max. - */ -enum LODLevel -{ - LOD_Constant = -1, //!< triangle is always visible, no matter at what distance - LOD_Low = 1, //!< triangle is visible at farthest distance (lowest quality) - LOD_Medium = 2, //!< triangle is visible at medium distance (medium quality) - LOD_High = 4 //!< triangle is visible at closest distance (highest quality) -}; - -/** - * \struct ModelTriangle - * \brief Triangle of a 3D model - */ -struct ModelTriangle -{ - //! 1st vertex - VertexTex2 p1; - //! 2nd vertex - VertexTex2 p2; - //! 3rd vertex - VertexTex2 p3; - //! Material - Material material; - //! Name of 1st texture - std::string tex1Name; - //! Name of 2nd texture - std::string tex2Name; - //! If true, 2nd texture will be taken from current engine setting - bool variableTex2; - //! LOD level - LODLevel lodLevel; - //! Rendering state to be set - int state; - - ModelTriangle() - { - variableTex2 = true; - lodLevel = LOD_Constant; - state = 0; - } -}; - - -/** - * \class CModelFile - * \brief Model file reader/writer - * - * Allows reading and writing model objects. Models are collections of ModelTriangle structs. - */ -class CModelFile -{ -public: - CModelFile(); - ~CModelFile(); - - //! Reads a model in text format from file - bool ReadTextModel(const std::string &fileName); - //! Reads a model in text format from stream - bool ReadTextModel(std::istream &stream); - - //! Writes the model in text format to a file - bool WriteTextModel(const std::string &fileName); - //! Writes the model in text format to a stream - bool WriteTextModel(std::ostream &stream); - - //! Reads a model in new binary format from file - bool ReadBinaryModel(const std::string &fileName); - //! Reads a model in new binary format from stream - bool ReadBinaryModel(std::istream &stream); - - //! Writes the model in binary format to a file - bool WriteBinaryModel(const std::string &fileName); - //! Writes the model in binary format to a stream - bool WriteBinaryModel(std::ostream &stream); - - //! Reads a binary Colobot model from file - //! @deprecated - bool ReadModel(const std::string &fileName); - //! Reads a binary Colobot model from stream - //! @deprecated - bool ReadModel(std::istream &stream); - //! Writes the model to Colobot binary model file - //! @deprecated - bool WriteModel(const std::string &fileName); - //! Writes the model to Colobot binary model file - //! @deprecated - bool WriteModel(std::ostream &stream); - - //! Returns the number of triangles in model - int GetTriangleCount(); - - //! Returns the triangle vector - const std::vector& GetTriangles(); - - //! Controls printing of debug information - void SetPrintDebugInfo(bool printDebugInfo); - -protected: - //@{ - //! @deprecated min, max conversions - LODLevel MinMaxToLodLevel(float min, float max); - void LODLevelToMinMax(LODLevel lodLevel, float& min, float& max); - //@} - -protected: - //! Model triangles - std::vector m_triangles; - bool m_printDebugInfo; -}; - -}; // namespace Gfx - diff --git a/src/graphics/engine/modelmanager.cpp b/src/graphics/engine/oldmodelmanager.cpp similarity index 52% rename from src/graphics/engine/modelmanager.cpp rename to src/graphics/engine/oldmodelmanager.cpp index 29ccb878..b1449816 100644 --- a/src/graphics/engine/modelmanager.cpp +++ b/src/graphics/engine/oldmodelmanager.cpp @@ -17,45 +17,57 @@ * along with this program. If not, see http://gnu.org/licenses */ -#include "graphics/engine/modelmanager.h" +#include "graphics/engine/oldmodelmanager.h" #include "app/app.h" #include "common/logger.h" +#include "common/resources/inputstream.h" #include "graphics/engine/engine.h" +#include "graphics/model/model_input.h" +#include "graphics/model/model_io_exception.h" + #include namespace Gfx { -CModelManager::CModelManager(CEngine* engine) +COldModelManager::COldModelManager(CEngine* engine) { m_engine = engine; } -CModelManager::~CModelManager() +COldModelManager::~COldModelManager() { } -bool CModelManager::LoadModel(const std::string& fileName, bool mirrored) +bool COldModelManager::LoadModel(const std::string& fileName, bool mirrored) { GetLogger()->Debug("Loading model '%s'\n", fileName.c_str()); - CModelFile modelFile; - - if (CApplication::GetInstance().IsDebugModeActive(DEBUG_MODELS)) - modelFile.SetPrintDebugInfo(true); - - if (!modelFile.ReadModel("models/" + fileName)) + CModel model; + try { - GetLogger()->Error("Loading model '%s' failed\n", fileName.c_str()); + CInputStream stream; + stream.open("models/" + fileName); + if (!stream.is_open()) + throw CModelIOException(std::string("Could not open file '") + fileName + "'"); + + model = ModelInput::Read(stream, ModelFormat::Old); + } + catch (const CModelIOException& e) + { + GetLogger()->Error("Loading model '%s' failed: %s\n", fileName.c_str(), e.what()); return false; } + CModelMesh* mesh = model.GetMesh("main"); + assert(mesh != nullptr); + ModelInfo modelInfo; modelInfo.baseObjRank = m_engine->CreateBaseObject(); - modelInfo.triangles = modelFile.GetTriangles(); + modelInfo.triangles = mesh->GetTriangles(); if (mirrored) Mirror(modelInfo.triangles); @@ -65,41 +77,37 @@ bool CModelManager::LoadModel(const std::string& fileName, bool mirrored) std::vector vs(3, VertexTex2()); - for (int i = 0; i < static_cast( modelInfo.triangles.size() ); i++) + for (const auto& triangle : modelInfo.triangles) { - int state = modelInfo.triangles[i].state; - std::string tex1Name = "objects/"+modelInfo.triangles[i].tex1Name; - if(modelInfo.triangles[i].tex1Name.empty()) - tex1Name.clear(); - std::string tex2Name = modelInfo.triangles[i].tex2Name; + vs[0] = triangle.p1; + vs[1] = triangle.p2; + vs[2] = triangle.p3; - if (modelInfo.triangles[i].variableTex2) - { - state |= ENG_RSTATE_DUAL_BLACK; + Material material; + material.ambient = triangle.ambient; + material.diffuse = triangle.diffuse; + material.specular = triangle.specular; - /*TODO: This seems to be not used by Colobot - if (texNum >= 1 && texNum <= 10) - state |= ENG_RSTATE_DUAL_BLACK; + int state = GetEngineState(triangle); - if (texNum >= 11 && texNum <= 20) - state |= ENG_RSTATE_DUAL_WHITE;*/ + std::string tex1Name; + if (!triangle.tex1Name.empty()) + tex1Name = "objects/" + triangle.tex1Name; + + std::string tex2Name; + if (triangle.variableTex2) tex2Name = m_engine->GetSecondTexture(); - } - - vs[0] = modelInfo.triangles[i].p1; - vs[1] = modelInfo.triangles[i].p2; - vs[2] = modelInfo.triangles[i].p3; + else + tex2Name = triangle.tex2Name; m_engine->AddBaseObjTriangles(modelInfo.baseObjRank, vs, ENG_TRIANGLE_TYPE_TRIANGLES, - modelInfo.triangles[i].material, state, - tex1Name, tex2Name, - modelInfo.triangles[i].lodLevel, false); + material, state, tex1Name, tex2Name, false); } return true; } -bool CModelManager::AddModelReference(const std::string& fileName, bool mirrored, int objRank) +bool COldModelManager::AddModelReference(const std::string& fileName, bool mirrored, int objRank) { auto it = m_models.find(FileInfo(fileName, mirrored)); if (it == m_models.end()) @@ -115,7 +123,7 @@ bool CModelManager::AddModelReference(const std::string& fileName, bool mirrored return true; } -bool CModelManager::AddModelCopy(const std::string& fileName, bool mirrored, int objRank) +bool COldModelManager::AddModelCopy(const std::string& fileName, bool mirrored, int objRank) { auto it = m_models.find(FileInfo(fileName, mirrored)); if (it == m_models.end()) @@ -135,12 +143,12 @@ bool CModelManager::AddModelCopy(const std::string& fileName, bool mirrored, int return true; } -bool CModelManager::IsModelLoaded(const std::string& fileName, bool mirrored) +bool COldModelManager::IsModelLoaded(const std::string& fileName, bool mirrored) { return m_models.count(FileInfo(fileName, mirrored)) > 0; } -int CModelManager::GetModelBaseObjRank(const std::string& fileName, bool mirrored) +int COldModelManager::GetModelBaseObjRank(const std::string& fileName, bool mirrored) { auto it = m_models.find(FileInfo(fileName, mirrored)); if (it == m_models.end()) @@ -149,7 +157,7 @@ int CModelManager::GetModelBaseObjRank(const std::string& fileName, bool mirrore return (*it).second.baseObjRank; } -void CModelManager::DeleteAllModelCopies() +void COldModelManager::DeleteAllModelCopies() { for (int baseObjRank : m_copiesBaseRanks) { @@ -159,7 +167,7 @@ void CModelManager::DeleteAllModelCopies() m_copiesBaseRanks.clear(); } -void CModelManager::UnloadModel(const std::string& fileName, bool mirrored) +void COldModelManager::UnloadModel(const std::string& fileName, bool mirrored) { auto it = m_models.find(FileInfo(fileName, mirrored)); if (it == m_models.end()) @@ -170,7 +178,7 @@ void CModelManager::UnloadModel(const std::string& fileName, bool mirrored) m_models.erase(it); } -void CModelManager::UnloadAllModels() +void COldModelManager::UnloadAllModels() { for (auto& mf : m_models) m_engine->DeleteBaseObject(mf.second.baseObjRank); @@ -178,7 +186,7 @@ void CModelManager::UnloadAllModels() m_models.clear(); } -void CModelManager::Mirror(std::vector& triangles) +void COldModelManager::Mirror(std::vector& triangles) { for (int i = 0; i < static_cast( triangles.size() ); i++) { @@ -196,28 +204,54 @@ void CModelManager::Mirror(std::vector& triangles) } } -float CModelManager::GetHeight(std::vector& triangles, Math::Vector pos) +int COldModelManager::GetEngineState(const ModelTriangle& triangle) { - const float limit = 5.0f; + int state = 0; - for (int i = 0; i < static_cast( triangles.size() ); i++) + if (!triangle.tex2Name.empty() || triangle.variableTex2) + state |= ENG_RSTATE_DUAL_BLACK; + + switch (triangle.transparentMode) { - if ( fabs(pos.x - triangles[i].p1.coord.x) < limit && - fabs(pos.z - triangles[i].p1.coord.z) < limit ) - return triangles[i].p1.coord.y; + case ModelTransparentMode::None: + break; - if ( fabs(pos.x - triangles[i].p2.coord.x) < limit && - fabs(pos.z - triangles[i].p2.coord.z) < limit ) - return triangles[i].p2.coord.y; + case ModelTransparentMode::AlphaChannel: + state |= ENG_RSTATE_ALPHA; + break; - if ( fabs(pos.x - triangles[i].p3.coord.x) < limit && - fabs(pos.z - triangles[i].p3.coord.z) < limit ) - return triangles[i].p3.coord.y; + case ModelTransparentMode::MapBlackToAlpha: + state |= ENG_RSTATE_TTEXTURE_BLACK; + break; + + case ModelTransparentMode::MapWhiteToAlpha: + state |= ENG_RSTATE_TTEXTURE_WHITE; + break; } - return 0.0f; -} + switch (triangle.specialMark) + { + case ModelSpecialMark::None: + break; + case ModelSpecialMark::Part1: + state |= ENG_RSTATE_PART1; + break; + + case ModelSpecialMark::Part2: + state |= ENG_RSTATE_PART2; + break; + + case ModelSpecialMark::Part3: + state |= ENG_RSTATE_PART3; + break; + } + + if (triangle.doubleSided) + state |= ENG_RSTATE_2FACE; + + return state; +} } diff --git a/src/graphics/engine/modelmanager.h b/src/graphics/engine/oldmodelmanager.h similarity index 92% rename from src/graphics/engine/modelmanager.h rename to src/graphics/engine/oldmodelmanager.h index e075dd54..c41ca7b7 100644 --- a/src/graphics/engine/modelmanager.h +++ b/src/graphics/engine/oldmodelmanager.h @@ -21,7 +21,7 @@ #include "common/singleton.h" -#include "graphics/engine/modelfile.h" +#include "graphics/model/model_triangle.h" #include #include @@ -33,7 +33,7 @@ class CEngine; class CModelFile; /** - * \class CModelManager + * \class COldModelManager * \brief Manager for static models * * The manager allows for loading models as static objects and adding @@ -51,11 +51,11 @@ class CModelFile; * its own and unique base engine object. This is especially useful * for models where the geometry must be altered. */ -class CModelManager +class COldModelManager { public: - CModelManager(CEngine* engine); - ~CModelManager(); + COldModelManager(CEngine* engine); + ~COldModelManager(); //! Loads a model from given file bool LoadModel(const std::string& fileName, bool mirrored); @@ -81,12 +81,12 @@ public: void UnloadAllModels(); protected: - //! Returns the height of model -- closest point to X and Z coords of \a pos - float GetHeight(std::vector& triangles, Math::Vector pos); - //! Mirrors the model along the Z axis void Mirror(std::vector& triangles); + //! Converts from model to engine rendering state + int GetEngineState(const ModelTriangle& triangle); + private: struct ModelInfo { diff --git a/src/graphics/engine/pyro.cpp b/src/graphics/engine/pyro.cpp index dbe49ddc..bc7fe782 100644 --- a/src/graphics/engine/pyro.cpp +++ b/src/graphics/engine/pyro.cpp @@ -1427,17 +1427,8 @@ void CPyro::CreateTriangle(CObject* obj, ObjectType oType, int part) percent = 0.50f; } - LODLevel lodLevel = LOD_High; - - if (oType == OBJECT_MOTHER || - oType == OBJECT_TEEN28 || - oType == OBJECT_TEEN31) - { - lodLevel = LOD_Constant; - } - std::vector buffer; - total = m_engine->GetPartialTriangles(objRank, lodLevel, percent, 100, buffer); + total = m_engine->GetPartialTriangles(objRank, percent, 100, buffer); for (int i = 0; i < total; i++) { diff --git a/src/graphics/engine/terrain.cpp b/src/graphics/engine/terrain.cpp index 58c2a605..5f61f521 100644 --- a/src/graphics/engine/terrain.cpp +++ b/src/graphics/engine/terrain.cpp @@ -716,7 +716,7 @@ bool CTerrain::CreateMosaic(int ox, int oy, int step, int objRank, buffer.vertices.push_back(p2); } - m_engine->AddBaseObjQuick(baseObjRank, buffer, texName1, texName2, LOD_Constant, true); + m_engine->AddBaseObjQuick(baseObjRank, buffer, texName1, texName2, true); } } } diff --git a/src/graphics/model/model.cpp b/src/graphics/model/model.cpp new file mode 100644 index 00000000..f657ff65 --- /dev/null +++ b/src/graphics/model/model.cpp @@ -0,0 +1,60 @@ +#include "graphics/model/model.h" + +#include "common/logger.h" + + +namespace Gfx { + +int CModel::GetMeshCount() const +{ + return m_meshes.size(); +} + +CModelMesh* CModel::GetMesh(const std::string& name) +{ + auto it = m_meshes.find(name); + if (it == m_meshes.end()) + { + GetLogger()->Error("Mesh named '%s' not found in model!\n", name.c_str()); + return nullptr; + } + return &(it->second); +} + +const CModelMesh* CModel::GetMesh(const std::string& name) const +{ + auto it = m_meshes.find(name); + if (it == m_meshes.end()) + { + GetLogger()->Error("Mesh named '%s' not found in model!\n", name.c_str()); + return nullptr; + } + return &(it->second); +} + +void CModel::AddMesh(const std::string& name, CModelMesh&& mesh) +{ + m_meshes[name] = mesh; +} + +const boost::optional& CModel::GetShadowSpot() const +{ + return m_shadowSpot; +} + +void CModel::SetShadowSpot(const ModelShadowSpot& shadowSpot) +{ + m_shadowSpot = shadowSpot; +} + +const std::vector& CModel::GetCrashSpheres() const +{ + return m_crashSpheres; +} + +void CModel::AddCrashSphere(const ModelCrashSphere& crashSphere) +{ + m_crashSpheres.push_back(crashSphere); +} + +} // namespace Gfx diff --git a/src/graphics/model/model.h b/src/graphics/model/model.h new file mode 100644 index 00000000..3031e179 --- /dev/null +++ b/src/graphics/model/model.h @@ -0,0 +1,46 @@ +#pragma once + +#include "graphics/model/model_crash_sphere.h" +#include "graphics/model/model_mesh.h" +#include "graphics/model/model_shadow_spot.h" + +#include +#include +#include +#include + +namespace Gfx { + +/** + * \class CModel + * \brief 3D model saved in model file + */ +class CModel +{ +public: + //! Returns mesh count + int GetMeshCount() const; + //! Return a mesh with given \a name + CModelMesh* GetMesh(const std::string& name); + //! Return a mesh with given \a name + const CModelMesh* GetMesh(const std::string& name) const; + //! Add new \a mesh with given \a name + void AddMesh(const std::string& name, CModelMesh&& mesh); + + //! Returns the optional shadow spot associated with model + const boost::optional& GetShadowSpot() const; + //! Sets the shadow spot associated with model + void SetShadowSpot(const ModelShadowSpot& shadowSpot); + + //! Returns the model's crash spheres + const std::vector& GetCrashSpheres() const; + //! Adds a new crash sphere + void AddCrashSphere(const ModelCrashSphere& crashSphere); + +private: + std::map m_meshes; + boost::optional m_shadowSpot; + std::vector m_crashSpheres; +}; + +} // namespace Gfx diff --git a/src/graphics/model/model_crash_sphere.h b/src/graphics/model/model_crash_sphere.h new file mode 100644 index 00000000..6a887919 --- /dev/null +++ b/src/graphics/model/model_crash_sphere.h @@ -0,0 +1,19 @@ +#pragma once + +#include "math/vector.h" + +namespace Gfx { + +/** + * \struct ModelCrashSphere + * \brief Crash sphere data as saved in model file + */ +struct ModelCrashSphere +{ + Math::Vector position; + float radius = 0.0f; + std::string sound; + float hardness = 0.0f; +}; + +} // namespace Gfx diff --git a/src/graphics/model/model_format.h b/src/graphics/model/model_format.h new file mode 100644 index 00000000..a4649019 --- /dev/null +++ b/src/graphics/model/model_format.h @@ -0,0 +1,16 @@ +#pragma once + +namespace Gfx { + +/** + * \enum ModelFormat + * \brief Describes model format to use + */ +enum class ModelFormat +{ + Text, //!< new text format + Binary, //!< new binary format + Old //!< old binary format, deprecated +}; + +} // namespace Gfx diff --git a/src/graphics/model/model_input.cpp b/src/graphics/model/model_input.cpp new file mode 100644 index 00000000..d85bfbf3 --- /dev/null +++ b/src/graphics/model/model_input.cpp @@ -0,0 +1,670 @@ +#include "graphics/model/model_input.h" + +#include "common/ioutils.h" +#include "common/logger.h" +#include "common/resources/inputstream.h" + +#include "graphics/model/model_io_exception.h" +#include "graphics/model/model_io_structs.h" + +#include +#include + +#include +#include + +namespace Gfx { + +// Private functions +namespace ModelInput +{ + void ReadTextModel(CModel &model, std::istream &stream); + void ReadTextModelV1AndV2(CModel &model, std::istream &stream); + void ReadTextModelV3(CModel &model, std::istream &stream); + + void ReadBinaryModel(CModel &model, std::istream &stream); + void ReadBinaryModelV1AndV2(CModel &model, std::istream &stream); + void ReadBinaryModelV3(CModel &model, std::istream &stream); + + void ReadOldModel(CModel &model, std::istream &stream); + std::vector ReadOldModelV1(std::istream &stream, int totalTriangles); + std::vector ReadOldModelV2(std::istream &stream, int totalTriangles); + std::vector ReadOldModelV3(std::istream &stream, int totalTriangles); + + Vertex ReadBinaryVertex(std::istream& stream); + VertexTex2 ReadBinaryVertexTex2(std::istream& stream); + Material ReadBinaryMaterial(std::istream& stream); + + std::string ReadLineString(std::istream& stream, const std::string& prefix); + VertexTex2 ParseVertexTex2(const std::string& text); + Material ParseTextMaterial(const std::string& text); + + void ConvertOldTex1Name(ModelTriangle& triangle, const char* tex1Name); + void ConvertFromOldRenderState(ModelTriangle& triangle, int state); + ModelLODLevel MinMaxToLodLevel(float min, float max); +} + +using namespace IOUtils; + +CModel ModelInput::Read(std::istream &stream, ModelFormat format) +{ + stream.exceptions(std::ios_base::failbit | std::ios_base::badbit); + + CModel model; + + try + { + switch (format) + { + case ModelFormat::Binary: + ReadBinaryModel(model, stream); + break; + + case ModelFormat::Text: + ReadTextModel(model, stream); + break; + + case ModelFormat::Old: + ReadOldModel(model, stream); + break; + } + } + catch (const CModelIOException& e) + { + throw; + } + catch (const std::exception& e) + { + throw CModelIOException(std::string("Other error while reading model data: ") + e.what()); + } + + return model; +} + +void ModelInput::ReadBinaryModel(CModel &model, std::istream &stream) +{ + int version = 0; + try + { + version = ReadBinary<4, int>(stream); + stream.seekg(std::ios_base::beg); + } + catch (const std::exception& e) + { + throw CModelIOException(std::string("Error reading version number: ") + e.what()); + } + + if (version == 1 || version == 2) + ReadBinaryModelV1AndV2(model, stream); + else if (version == 3) + ReadBinaryModelV3(model, stream); + else + throw CModelIOException(std::string("Unexpected version number: ") + std::to_string(version)); +} + +void ModelInput::ReadBinaryModelV1AndV2(CModel &model, std::istream &stream) +{ + ModelHeaderV1AndV2 header; + try + { + header.version = ReadBinary<4, int>(stream); + header.totalTriangles = ReadBinary<4, int>(stream); + } + catch (const std::exception& e) + { + throw CModelIOException(std::string("Error reading model file header: ") + e.what()); + } + + CModelMesh mesh; + + try + { + for (int i = 0; i < header.totalTriangles; ++i) + { + ModelTriangleV1AndV2 t; + + t.p1 = ReadBinaryVertexTex2(stream); + t.p2 = ReadBinaryVertexTex2(stream); + t.p3 = ReadBinaryVertexTex2(stream); + t.material = ReadBinaryMaterial(stream); + t.tex1Name = ReadBinaryString<1>(stream); + t.tex2Name = ReadBinaryString<1>(stream); + t.variableTex2 = ReadBinaryBool(stream); + + if (header.version == 1) + t.lodLevel = static_cast( ReadBinary<4, int>(stream) ); + + t.state = ReadBinary<4, int>(stream); + + if (t.lodLevel == ModelLODLevel::Low || + t.lodLevel == ModelLODLevel::Medium) + continue; + + ModelTriangle triangle; + triangle.p1 = t.p1; + triangle.p2 = t.p2; + triangle.p3 = t.p3; + triangle.diffuse = t.material.diffuse; + triangle.specular = t.material.specular; + triangle.ambient = t.material.ambient; + triangle.tex1Name = t.tex1Name; + triangle.tex2Name = t.tex2Name; + triangle.variableTex2 = t.variableTex2; + ConvertFromOldRenderState(triangle, t.state); + + mesh.AddTriangle(triangle); + } + } + catch (const std::exception& e) + { + throw CModelIOException(std::string("Error reading model data: ") + e.what()); + } + + model.AddMesh("main", std::move(mesh)); +} + +void ModelInput::ReadBinaryModelV3(CModel &model, std::istream &stream) +{ + // TODO... +} + +void ModelInput::ReadTextModel(CModel &model, std::istream &stream) +{ + int version = 0; + try + { + version = boost::lexical_cast(ReadLineString(stream, "version")); + stream.seekg(std::ios_base::beg); + } + catch (const std::exception& e) + { + throw CModelIOException(std::string("Error reading version number: ") + e.what()); + } + + if (version == 1 || version == 2) + ReadTextModelV1AndV2(model, stream); + else if (version == 3) + ReadTextModelV3(model, stream); + else + throw CModelIOException(std::string("Unexpected version number: ") + std::to_string(version)); +} + +void ModelInput::ReadTextModelV1AndV2(CModel &model, std::istream &stream) +{ + ModelHeaderV1AndV2 header; + + try + { + header.version = boost::lexical_cast(ReadLineString(stream, "version")); + header.totalTriangles = boost::lexical_cast(ReadLineString(stream, "total_triangles")); + } + catch (const std::exception& e) + { + throw CModelIOException(std::string("Error reading model header: ") + e.what()); + } + + CModelMesh mesh; + + for (int i = 0; i < header.totalTriangles; ++i) + { + ModelTriangleV1AndV2 t; + + std::string p1Text = ReadLineString(stream, "p1"); + t.p1 = ParseVertexTex2(p1Text); + std::string p2Text = ReadLineString(stream, "p2"); + t.p2 = ParseVertexTex2(p2Text); + std::string p3Text = ReadLineString(stream, "p3"); + t.p3 = ParseVertexTex2(p3Text); + + std::string matText = ReadLineString(stream, "mat"); + t.material = ParseTextMaterial(matText); + + t.tex1Name = ReadLineString(stream, "tex1"); + t.tex2Name = ReadLineString(stream, "tex2"); + std::string varTex2Ch = ReadLineString(stream, "var_tex2"); + t.variableTex2 = varTex2Ch == "Y"; + + if (header.version == 1) + t.lodLevel = static_cast( boost::lexical_cast(ReadLineString(stream, "lod_level")) ); + + t.state = boost::lexical_cast(ReadLineString(stream, "state")); + + if (t.lodLevel == ModelLODLevel::Low || + t.lodLevel == ModelLODLevel::Medium) + continue; + + ModelTriangle triangle; + triangle.p1 = t.p1; + triangle.p2 = t.p2; + triangle.p3 = t.p3; + triangle.ambient = t.material.ambient; + triangle.diffuse = t.material.diffuse; + triangle.specular = t.material.specular; + triangle.tex1Name = t.tex1Name; + triangle.tex2Name = t.tex2Name; + triangle.variableTex2 = t.variableTex2; + ConvertFromOldRenderState(triangle, t.state); + + mesh.AddTriangle(triangle); + } + + model.AddMesh("main", std::move(mesh)); +} + +void ModelInput::ReadTextModelV3(CModel &model, std::istream &stream) +{ + // TODO... +} + +void ModelInput::ReadOldModel(CModel &model, std::istream &stream) +{ + OldModelHeader header; + + try + { + header.revision = ReadBinary<4, int>(stream); + header.version = ReadBinary<4, int>(stream); + header.totalTriangles = ReadBinary<4, int>(stream); + for (int i = 0; i < 10; ++i) + header.reserved[i] = ReadBinary<4, int>(stream); + } + catch (const std::exception& e) + { + throw CModelIOException(std::string("Error reading model file header: ") + e.what()); + } + + std::vector triangles; + + try + { + if (header.revision == 1 && header.version == 0) + { + triangles = ReadOldModelV1(stream, header.totalTriangles); + } + else if (header.revision == 1 && header.version == 1) + { + triangles = ReadOldModelV2(stream, header.totalTriangles); + } + else + { + triangles = ReadOldModelV3(stream, header.totalTriangles); + } + } + catch (const std::exception& e) + { + throw CModelIOException(std::string("Error reading model triangles: ") + e.what()); + } + + CModelMesh mesh; + mesh.SetTriangles(std::move(triangles)); + + model.AddMesh("main", std::move(mesh)); +} + +std::vector ModelInput::ReadOldModelV1(std::istream &stream, int totalTriangles) +{ + std::vector triangles; + + for (int i = 0; i < totalTriangles; ++i) + { + OldModelTriangleV1 t; + t.used = ReadBinary<1, char>(stream); + t.selected = ReadBinary<1, char>(stream); + + /* padding */ ReadBinary<2, unsigned int>(stream); + + t.p1 = ReadBinaryVertex(stream); + t.p2 = ReadBinaryVertex(stream); + t.p3 = ReadBinaryVertex(stream); + + t.material = ReadBinaryMaterial(stream); + stream.read(t.texName, 20); + t.min = ReadBinaryFloat(stream); + t.max = ReadBinaryFloat(stream); + + ModelLODLevel lodLevel = MinMaxToLodLevel(t.min, t.max); + if (lodLevel == ModelLODLevel::Low || + lodLevel == ModelLODLevel::Medium) + continue; + + ModelTriangle triangle; + triangle.p1.FromVertex(t.p1); + triangle.p2.FromVertex(t.p2); + triangle.p3.FromVertex(t.p3); + + triangle.ambient = t.material.ambient; + triangle.diffuse = t.material.diffuse; + triangle.specular = t.material.specular; + ConvertOldTex1Name(triangle, t.texName); + + triangles.push_back(triangle); + } + + return triangles; +} + +std::vector ModelInput::ReadOldModelV2(std::istream &stream, int totalTriangles) +{ + std::vector triangles; + + for (int i = 0; i < totalTriangles; ++i) + { + OldModelTriangleV2 t; + t.used = ReadBinary<1, char>(stream); + t.selected = ReadBinary<1, char>(stream); + + /* padding */ ReadBinary<2, unsigned int>(stream); + + t.p1 = ReadBinaryVertex(stream); + t.p2 = ReadBinaryVertex(stream); + t.p3 = ReadBinaryVertex(stream); + + t.material = ReadBinaryMaterial(stream); + stream.read(t.texName, 20); + t.min = ReadBinaryFloat(stream); + t.max = ReadBinaryFloat(stream); + t.state = ReadBinary<4, long>(stream); + + t.reserved1 = ReadBinary<2, short>(stream); + t.reserved2 = ReadBinary<2, short>(stream); + t.reserved3 = ReadBinary<2, short>(stream); + t.reserved4 = ReadBinary<2, short>(stream); + + ModelLODLevel lodLevel = MinMaxToLodLevel(t.min, t.max); + if (lodLevel == ModelLODLevel::Low || + lodLevel == ModelLODLevel::Medium) + continue; + + ModelTriangle triangle; + triangle.p1.FromVertex(t.p1); + triangle.p2.FromVertex(t.p2); + triangle.p3.FromVertex(t.p3); + + triangle.ambient = t.material.ambient; + triangle.diffuse = t.material.diffuse; + triangle.specular = t.material.specular; + ConvertOldTex1Name(triangle, t.texName); + + ConvertFromOldRenderState(triangle, t.state); + + triangles.push_back(triangle); + } + + return triangles; +} + +std::vector ModelInput::ReadOldModelV3(std::istream &stream, int totalTriangles) +{ + std::vector triangles; + + for (int i = 0; i < totalTriangles; ++i) + { + OldModelTriangleV3 t; + t.used = ReadBinary<1, char>(stream); + t.selected = ReadBinary<1, char>(stream); + + /* padding */ ReadBinary<2, unsigned int>(stream); + + t.p1 = ReadBinaryVertexTex2(stream); + t.p2 = ReadBinaryVertexTex2(stream); + t.p3 = ReadBinaryVertexTex2(stream); + + t.material = ReadBinaryMaterial(stream); + stream.read(t.texName, 20); + t.min = ReadBinaryFloat(stream); + t.max = ReadBinaryFloat(stream); + t.state = ReadBinary<4, long>(stream); + t.texNum2 = ReadBinary<2, short>(stream); + + t.reserved2 = ReadBinary<2, short>(stream); + t.reserved3 = ReadBinary<2, short>(stream); + t.reserved4 = ReadBinary<2, short>(stream); + + ModelLODLevel lodLevel = MinMaxToLodLevel(t.min, t.max); + if (lodLevel == ModelLODLevel::Low || + lodLevel == ModelLODLevel::Medium) + continue; + + ModelTriangle triangle; + triangle.p1 = t.p1; + triangle.p2 = t.p2; + triangle.p3 = t.p3; + + triangle.ambient = t.material.ambient; + triangle.diffuse = t.material.diffuse; + triangle.specular = t.material.specular; + ConvertOldTex1Name(triangle, t.texName); + + ConvertFromOldRenderState(triangle, t.state); + triangle.variableTex2 = t.texNum2 == 1; + + if (!triangle.variableTex2 && t.texNum2 != 0) + { + char tex2Name[20] = { 0 }; + std::sprintf(tex2Name, "dirty%.2d.png", t.texNum2); + triangle.tex2Name = tex2Name; + } + + triangles.push_back(triangle); + } + + return triangles; +} + +ModelLODLevel ModelInput::MinMaxToLodLevel(float min, float max) +{ + if (min == 0.0f && max == 100.0f) + return ModelLODLevel::High; + else if (min == 100.0f && max == 200.0f) + return ModelLODLevel::Medium; + else if (min == 200.0f && max == 1000000.0f) + return ModelLODLevel::Low; + else if (min == 0.0f && max == 1000000.0f) + return ModelLODLevel::Constant; + + return ModelLODLevel::Constant; +} + +void ModelInput::ConvertOldTex1Name(ModelTriangle& triangle, const char* tex1Name) +{ + triangle.tex1Name = tex1Name; + boost::replace_all(triangle.tex1Name, "bmp", "png"); + boost::replace_all(triangle.tex1Name, "tga", "png"); +} + +void ModelInput::ConvertFromOldRenderState(ModelTriangle& triangle, int state) +{ + if (triangle.tex1Name == "plant.png" || (state & static_cast(ModelRenderState::Alpha)) != 0) + triangle.transparentMode = ModelTransparentMode::AlphaChannel; + else if ((state & static_cast(ModelRenderState::TTextureBlack)) != 0) + triangle.transparentMode = ModelTransparentMode::MapBlackToAlpha; + else if ((state & static_cast(ModelRenderState::TTextureWhite)) != 0) + triangle.transparentMode = ModelTransparentMode::MapWhiteToAlpha; + else + triangle.transparentMode = ModelTransparentMode::None; + + if ((state & static_cast(ModelRenderState::Part1)) != 0) + triangle.specialMark = ModelSpecialMark::Part1; + else if ((state & static_cast(ModelRenderState::Part2)) != 0) + triangle.specialMark = ModelSpecialMark::Part2; + else if ((state & static_cast(ModelRenderState::Part3)) != 0) + triangle.specialMark = ModelSpecialMark::Part3; + else + triangle.specialMark = ModelSpecialMark::None; + + triangle.doubleSided = (state & static_cast(ModelRenderState::TwoFace)) != 0; +} + +Vertex ModelInput::ReadBinaryVertex(std::istream& stream) +{ + Vertex vertex; + + vertex.coord.x = ReadBinaryFloat(stream); + vertex.coord.y = ReadBinaryFloat(stream); + vertex.coord.z = ReadBinaryFloat(stream); + + vertex.normal.x = ReadBinaryFloat(stream); + vertex.normal.y = ReadBinaryFloat(stream); + vertex.normal.z = ReadBinaryFloat(stream); + + vertex.texCoord.x = ReadBinaryFloat(stream); + vertex.texCoord.y = ReadBinaryFloat(stream); + + return vertex; +} + +VertexTex2 ModelInput::ReadBinaryVertexTex2(std::istream& stream) +{ + VertexTex2 vertex; + + vertex.coord.x = ReadBinaryFloat(stream); + vertex.coord.y = ReadBinaryFloat(stream); + vertex.coord.z = ReadBinaryFloat(stream); + + vertex.normal.x = ReadBinaryFloat(stream); + vertex.normal.y = ReadBinaryFloat(stream); + vertex.normal.z = ReadBinaryFloat(stream); + + vertex.texCoord.x = ReadBinaryFloat(stream); + vertex.texCoord.y = ReadBinaryFloat(stream); + + vertex.texCoord2.x = ReadBinaryFloat(stream); + vertex.texCoord2.y = ReadBinaryFloat(stream); + + return vertex; +} + +Material ModelInput::ReadBinaryMaterial(std::istream& stream) +{ + Material material; + + material.diffuse.r = ReadBinaryFloat(stream); + material.diffuse.g = ReadBinaryFloat(stream); + material.diffuse.b = ReadBinaryFloat(stream); + material.diffuse.a = ReadBinaryFloat(stream); + + material.ambient.r = ReadBinaryFloat(stream); + material.ambient.g = ReadBinaryFloat(stream); + material.ambient.b = ReadBinaryFloat(stream); + material.ambient.a = ReadBinaryFloat(stream); + + material.specular.r = ReadBinaryFloat(stream); + material.specular.g = ReadBinaryFloat(stream); + material.specular.b = ReadBinaryFloat(stream); + material.specular.a = ReadBinaryFloat(stream); + + /* emissive.r = */ ReadBinaryFloat(stream); + /* emissive.g = */ ReadBinaryFloat(stream); + /* emissive.b = */ ReadBinaryFloat(stream); + /* emissive.a = */ ReadBinaryFloat(stream); + + /* power = */ ReadBinaryFloat(stream); + + return material; +} + + +std::string ModelInput::ReadLineString(std::istream& stream, const std::string& expectedPrefix) +{ + std::string line; + while (true) + { + if (stream.eof()) + throw CModelIOException("Unexpected EOF"); + + std::getline(stream, line); + if (!line.empty() && line[0] != '#') + break; + } + + std::stringstream s; + s.str(line); + + std::string prefix; + s >> prefix; + if (prefix != expectedPrefix) + throw CModelIOException(std::string("Unexpected prefix: '") + prefix + "'"); + + std::string value; + std::getline(s, value); + boost::trim_left(value); + + return value; +} + +VertexTex2 ModelInput::ParseVertexTex2(const std::string& text) +{ + VertexTex2 vertex; + + std::stringstream stream; + stream.str(text); + + std::string prefix; + + stream >> prefix; + if (prefix != "c") + throw CModelIOException(std::string("Unexpected prefix: '") + prefix + "'"); + + stream >> vertex.coord.x >> vertex.coord.y >> vertex.coord.z; + + stream >> prefix; + if (prefix != "n") + throw CModelIOException(std::string("Unexpected prefix: '") + prefix + "'"); + + stream >> vertex.normal.x >> vertex.normal.y >> vertex.normal.z; + + stream >> prefix; + if (prefix != "t1") + throw CModelIOException(std::string("Unexpected prefix: '") + prefix + "'"); + + stream >> vertex.texCoord.x >> vertex.texCoord.y; + + stream >> prefix; + if (prefix != "t2") + throw CModelIOException(std::string("Unexpected prefix: '") + prefix + "'"); + + stream >> vertex.texCoord2.x >> vertex.texCoord2.y; + + return vertex; +} + +Material ModelInput::ParseTextMaterial(const std::string& text) +{ + Material material; + + std::stringstream stream; + stream.str(text); + + std::string prefix; + + stream >> prefix; + if (prefix != "dif") + throw CModelIOException(std::string("Unexpected prefix: '") + prefix + "'"); + + stream >> material.diffuse.r + >> material.diffuse.g + >> material.diffuse.b + >> material.diffuse.a; + + stream >> prefix; + if (prefix != "amb") + throw CModelIOException(std::string("Unexpected prefix: '") + prefix + "'"); + + stream >> material.ambient.r + >> material.ambient.g + >> material.ambient.b + >> material.ambient.a; + + stream >> prefix; + if (prefix != "spc") + throw CModelIOException(std::string("Unexpected prefix: '") + prefix + "'"); + + stream >> material.specular.r + >> material.specular.g + >> material.specular.b + >> material.specular.a; + + return material; +} + +} // namespace Gfx diff --git a/src/graphics/model/model_input.h b/src/graphics/model/model_input.h new file mode 100644 index 00000000..7267bf99 --- /dev/null +++ b/src/graphics/model/model_input.h @@ -0,0 +1,23 @@ +#pragma once + +#include "graphics/model/model.h" +#include "graphics/model/model_format.h" + +#include + +namespace Gfx { + +/** + * \namespace ModelInput + * \brief Namespace with functions to read model files + */ +namespace ModelInput +{ + //! Reads model from \a stream in given \a format + /** + * @throws CModelIOException on read/write error + */ + CModel Read(std::istream &stream, ModelFormat format); +} + +} // namespace Gfx diff --git a/src/graphics/model/model_io_exception.h b/src/graphics/model/model_io_exception.h new file mode 100644 index 00000000..4cc55543 --- /dev/null +++ b/src/graphics/model/model_io_exception.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace Gfx { + +class CModelIOException : public std::runtime_error +{ +public: + explicit CModelIOException(const std::string& error) + : std::runtime_error(error) + {} +}; + +} // namespace Gfx diff --git a/src/graphics/model/model_io_structs.h b/src/graphics/model/model_io_structs.h new file mode 100644 index 00000000..88f24ea5 --- /dev/null +++ b/src/graphics/model/model_io_structs.h @@ -0,0 +1,243 @@ +#pragma once + +#include "graphics/core/material.h" +#include "graphics/model/model_triangle.h" + +#include + +namespace Gfx { + +/******************************************************* + Deprecated enums/magic values + *******************************************************/ + +/** + * \enum ModelLODLevel + * \brief Old level-of-detail enum + * + * @deprecated + */ +enum class ModelLODLevel +{ + Constant = -1, //!< triangle is always visible, no matter at what distance + Low = 1, //!< triangle is visible at farthest distance (lowest quality) + Medium = 2, //!< triangle is visible at medium distance (medium quality) + High = 4 //!< triangle is visible at closest distance (highest quality) +}; + +/** + * \enum ModelRenderState + * \brief Old render state enum (values copied from EngineRenderState) + * + * @deprecated + */ +enum class ModelRenderState +{ + TTextureBlack = 1, //!< old ENG_RSTATE_TTEXTURE_BLACK + TTextureWhite = 2, //!< old ENG_RSTATE_TTEXTURE_WHITE + Part1 = 256, //!< old ENG_RSTATE_PART1 + Part2 = 512, //!< old ENG_RSTATE_PART2 + Part3 = 1024, //!< old ENG_RSTATE_PART3 + TwoFace = 4096, //!< old ENG_RSTATE_2FACE + Alpha = 8192 //!< old ENG_RSTATE_ALPHA +}; + +/******************************************************* + New model formats + *******************************************************/ + +/** + * \struct ModelHeaderV1AndV2 + * \brief Header for new model file version 1 and 2 + */ +struct ModelHeaderV1AndV2 +{ + //! File version (1, 2, ...) + int version = 0; + //! Total number of triangles + int totalTriangles = 0; +}; + +/** + * \struct ModelTriangleV1AndV2 + * \brief Triangle of new model file version 1 and 2 + */ +struct ModelTriangleV1AndV2 +{ + //! 1st vertex + VertexTex2 p1; + //! 2nd vertex + VertexTex2 p2; + //! 3rd vertex + VertexTex2 p3; + //! Material + Material material; + //! Name of 1st texture + std::string tex1Name; + //! Name of 2nd texture + std::string tex2Name; + //! If true, 2nd texture will be taken from current engine setting + bool variableTex2 = true; + //! LOD level (only version 1) + ModelLODLevel lodLevel = ModelLODLevel::Constant; + //! Rendering state to be set + int state = 0; +}; + +/** + * \struct ModelHeaderV3 + * \brief Header for new model file version 3 + */ +struct ModelHeaderV3 +{ + //! File version (1, 2, ...) + int version = 0; + //! Total number of meshes + int totalMeshes = 0; + //! Total number of crash spheres + int totalCrashSpheres = 0; + //! Whether model has shadow spot + bool shadowSpot = false; +}; + +/** + * \struct ModelMeshHeaderV3 + * \brief Header for mesh saved in new model file version 3 + */ +struct ModelMeshHeaderV3 +{ + //! Total number of triangles + int totalTriangles = 0; + //! Mesh name + std::string name; + //! Parent mesh name + std::string parentName; + //! Mesh position + Math::Vector position; + //! Mesh rotation + Math::Vector rotation; + //! Mesh scale + Math::Vector scale; +}; + +/** + * \struct ModelTriangleV3 + * \brief Mesh triangle saved in new model file version 3 + * + * NOTE: this is newest version, always same as ModelTriangle struct. + */ +struct ModelTriangleV3 : ModelTriangle {}; + + + +/******************************************************* + Deprecated formats + *******************************************************/ + +/** + * \struct OldModelHeader + * \brief Old Colobot binary model header info + * + * @deprecated + */ +struct OldModelHeader +{ + //! Revision number + int revision; + //! Version number + int version; + //! Total number of triangles + int totalTriangles; + //! Reserved area + int reserved[10]; + + OldModelHeader() + { + memset(this, 0, sizeof(*this)); + } +}; + + +/** + * \struct OldModelTriangleV1 + * \brief Old Colobot binary model file version 1 + * + * @deprecated + */ +struct OldModelTriangleV1 +{ + char used; + char selected; + Vertex p1; + Vertex p2; + Vertex p3; + Material material; + char texName[20]; + float min; + float max; + + OldModelTriangleV1() + { + memset(this, 0, sizeof(*this)); + } +}; + +/** + * \struct OldModelTriangleV2 + * \brief Old Colobot binary model file version 2 + * + * @deprecated + */ +struct OldModelTriangleV2 +{ + char used; + char selected; + Vertex p1; + Vertex p2; + Vertex p3; + Material material; + char texName[20]; + float min; + float max; + long state; + short reserved1; + short reserved2; + short reserved3; + short reserved4; + + OldModelTriangleV2() + { + memset(this, 0, sizeof(*this)); + } +}; + +/** + * \struct OldModelTriangleV3 + * \brief Old Colobot binary model file version 3 + * + * @deprecated + */ +struct OldModelTriangleV3 +{ + char used; + char selected; + VertexTex2 p1; + VertexTex2 p2; + VertexTex2 p3; + Material material; + char texName[20]; + float min; + float max; + long state; + short texNum2; + short reserved2; + short reserved3; + short reserved4; + + OldModelTriangleV3() + { + memset(this, 0, sizeof(*this)); + } +}; + +} // namespace Gfx diff --git a/src/graphics/model/model_manager.cpp b/src/graphics/model/model_manager.cpp new file mode 100644 index 00000000..4c1d561f --- /dev/null +++ b/src/graphics/model/model_manager.cpp @@ -0,0 +1,32 @@ +#include "graphics/model/model_manager.h" + +#include "common/resources/inputstream.h" + +#include "graphics/model/model_input.h" +#include "graphics/model/model_io_exception.h" + +namespace Gfx { + +CModel& CModelManager::GetModel(const std::string& fileName) +{ + auto it = m_models.find(fileName); + if (it != m_models.end()) + return it->second; + + CInputStream stream; + stream.open("models-new/" + fileName); + if (!stream.is_open()) + throw CModelIOException(std::string("Could not open file '") + fileName + "'"); + + CModel model = ModelInput::Read(stream, ModelFormat::Text); + m_models[fileName] = model; + + return m_models[fileName]; +} + +void CModelManager::ClearCache() +{ + m_models.clear(); +} + +} // namespace Gfx diff --git a/src/graphics/model/model_manager.h b/src/graphics/model/model_manager.h new file mode 100644 index 00000000..296455f5 --- /dev/null +++ b/src/graphics/model/model_manager.h @@ -0,0 +1,28 @@ +#pragma once + +#include "graphics/model/model.h" + +#include +#include + +namespace Gfx { + +/** + * \class CModelManager + * \brief Manager for models read from model files + */ +class CModelManager +{ +public: + //! Returns a model read from \a fileName + /** @throws CModelIOException on read error */ + CModel& GetModel(const std::string& fileName); + + //! Clears cached models + void ClearCache(); + +private: + std::unordered_map m_models; +}; + +} // namespace Gfx diff --git a/src/graphics/model/model_mesh.cpp b/src/graphics/model/model_mesh.cpp new file mode 100644 index 00000000..455a14de --- /dev/null +++ b/src/graphics/model/model_mesh.cpp @@ -0,0 +1,65 @@ +#include "graphics/model/model_mesh.h" + +namespace Gfx { + +void CModelMesh::AddTriangle(const ModelTriangle& triangle) +{ + m_triangles.push_back(triangle); +} + +void CModelMesh::SetTriangles(std::vector&& triangles) +{ + m_triangles = triangles; +} + +const std::vector& CModelMesh::GetTriangles() const +{ + return m_triangles; +} + +int CModelMesh::GetTriangleCount() const +{ + return m_triangles.size(); +} + +const Math::Vector& CModelMesh::GetPosition() const +{ + return m_position; +} + +void CModelMesh::SetPosition(const Math::Vector& position) +{ + m_position = position; +} + +const Math::Vector& CModelMesh::GetRotation() const +{ + return m_rotation; +} + +void CModelMesh::SetRotation(const Math::Vector& rotation) +{ + m_rotation = rotation; +} + +const Math::Vector& CModelMesh::GetScale() const +{ + return m_scale; +} + +void CModelMesh::SetScale(const Math::Vector& scale) +{ + m_scale = scale; +} + +const std::string& CModelMesh::GetParent() const +{ + return m_parent; +} + +void CModelMesh::SetParent(const std::string& parent) +{ + m_parent = parent; +} + +} // namespace Gfx diff --git a/src/graphics/model/model_mesh.h b/src/graphics/model/model_mesh.h new file mode 100644 index 00000000..7f710128 --- /dev/null +++ b/src/graphics/model/model_mesh.h @@ -0,0 +1,57 @@ +#pragma once + +#include "math/vector.h" + +#include "graphics/model/model_triangle.h" + +#include + +namespace Gfx { + +struct ModelTriangle; + +/** + * \class CModelMesh + * \brief Mesh data saved in model file + */ +class CModelMesh +{ +public: + //! Adds a new triangle + void AddTriangle(const ModelTriangle& triangle); + //! Sets the list of triangles + void SetTriangles(std::vector &&triangles); + //! Returns the list of triangles + const std::vector& GetTriangles() const; + //! Returns number of triangles + int GetTriangleCount() const; + + //! Returns the mesh position + const Math::Vector& GetPosition() const; + //! Sets the mesh rotation + void SetPosition(const Math::Vector& position); + + //! Returns the mesh rotation + const Math::Vector& GetRotation() const; + //! Sets the mesh rotation + void SetRotation(const Math::Vector& rotation); + + //! Returns the mesh scale + const Math::Vector& GetScale() const; + //! Sets the mesh scale + void SetScale(const Math::Vector& scale); + + //! Returns the name of parent mesh + const std::string& GetParent() const; + //! Sets the name of parent mesh + void SetParent(const std::string& parent); + +private: + std::vector m_triangles; + Math::Vector m_position; + Math::Vector m_rotation; + Math::Vector m_scale; + std::string m_parent; +}; + +} // namespace Gfx diff --git a/src/graphics/model/model_output.cpp b/src/graphics/model/model_output.cpp new file mode 100644 index 00000000..4365b4f7 --- /dev/null +++ b/src/graphics/model/model_output.cpp @@ -0,0 +1,338 @@ +#include "graphics/model/model_output.h" + +#include "common/ioutils.h" + +#include "graphics/model/model_io_exception.h" +#include "graphics/model/model_io_structs.h" + +#include "graphics/model/model.h" + +#include + +namespace Gfx { + +// Private functions +namespace ModelOutput +{ + void WriteTextModel(const CModel& model, std::ostream &stream); + + void WriteBinaryModel(const CModel& model, std::ostream &stream); + + void WriteOldModel(const CModel& model, std::ostream &stream); + + int ConvertToOldState(const ModelTriangle& triangle); + + void WriteBinaryVertexTex2(VertexTex2 vertex, std::ostream &stream); + void WriteBinaryMaterial(const Material& material, std::ostream &stream); + + void WriteTextVertexTex2(const VertexTex2& vertex, std::ostream &stream); + void WriteTextMaterial(const Material& material, std::ostream &stream); +} + +using namespace IOUtils; + +void ModelOutput::Write(const CModel& model, std::ostream &stream, ModelFormat format) +{ + stream.exceptions(std::ios_base::failbit | std::ios_base::badbit); + + try + { + switch (format) + { + case ModelFormat::Text: + WriteTextModel(model, stream); + break; + + case ModelFormat::Binary: + WriteBinaryModel(model, stream); + break; + + case ModelFormat::Old: + WriteOldModel(model, stream); + break; + } + } + catch (const CModelIOException& e) + { + throw; + } + catch (const std::exception& e) + { + throw CModelIOException(std::string("Error saving model data: ") + e.what()); + } +} + +void ModelOutput::WriteTextModel(const CModel& model, std::ostream &stream) +{ + const CModelMesh* mesh = model.GetMesh("main"); + if (mesh == nullptr) + throw CModelIOException("No main mesh found in model"); + + ModelHeaderV1AndV2 header; + + header.version = 2; + header.totalTriangles = mesh->GetTriangleCount(); + + stream << "# Colobot text model" << std::endl; + stream << std::endl; + stream << "### HEAD" << std::endl; + stream << "version " << header.version << std::endl; + stream << "total_triangles " << header.totalTriangles << std::endl; + stream << std::endl; + stream << "### TRIANGLES" << std::endl; + + for (const ModelTriangle& triangle : mesh->GetTriangles()) + { + ModelTriangleV1AndV2 t; + + t.p1 = triangle.p1; + t.p2 = triangle.p2; + t.p3 = triangle.p3; + t.material.ambient = triangle.ambient; + t.material.diffuse = triangle.diffuse; + t.material.specular = triangle.specular; + t.tex1Name = triangle.tex1Name; + t.tex2Name = triangle.tex2Name; + t.variableTex2 = triangle.variableTex2; + t.state = ConvertToOldState(triangle); + + stream << "p1 "; + WriteTextVertexTex2(t.p1, stream); + stream << "p2 "; + WriteTextVertexTex2(t.p2, stream); + stream << "p3 "; + WriteTextVertexTex2(t.p3, stream); + stream << "mat "; + WriteTextMaterial(t.material, stream); + + stream << "tex1 " << t.tex1Name << std::endl; + stream << "tex2 " << t.tex2Name << std::endl; + stream << "var_tex2 " << (t.variableTex2 ? 'Y' : 'N') << std::endl; + stream << "state " << t.state << std::endl; + + stream << std::endl; + } +} + +void ModelOutput::WriteBinaryModel(const CModel& model, std::ostream &stream) +{ + const CModelMesh* mesh = model.GetMesh("main"); + if (mesh == nullptr) + throw CModelIOException("No main mesh found in model"); + + ModelHeaderV1AndV2 header; + + header.version = 2; + header.totalTriangles = mesh->GetTriangleCount(); + + WriteBinary<4, int>(header.version, stream); + WriteBinary<4, int>(header.totalTriangles, stream); + + for (const ModelTriangle& triangle : mesh->GetTriangles()) + { + ModelTriangleV1AndV2 t; + + t.p1 = triangle.p1; + t.p2 = triangle.p2; + t.p3 = triangle.p3; + t.material.ambient = triangle.ambient; + t.material.diffuse = triangle.diffuse; + t.material.specular = triangle.specular; + t.tex1Name = triangle.tex1Name; + t.tex2Name = triangle.tex2Name; + t.variableTex2 = triangle.variableTex2; + t.state = ConvertToOldState(triangle); + + WriteBinaryVertexTex2(t.p1, stream); + WriteBinaryVertexTex2(t.p2, stream); + WriteBinaryVertexTex2(t.p3, stream); + WriteBinaryMaterial(t.material, stream); + WriteBinaryString<1>(t.tex1Name, stream); + WriteBinaryString<1>(t.tex2Name, stream); + WriteBinaryBool(t.variableTex2, stream); + WriteBinary<4, int>(t.state, stream); + } +} + +void ModelOutput::WriteOldModel(const CModel& model, std::ostream &stream) +{ + const CModelMesh* mesh = model.GetMesh("main"); + if (mesh == nullptr) + throw CModelIOException("No main mesh found in model"); + + OldModelHeader header; + header.revision = 1; + header.version = 2; + header.totalTriangles = mesh->GetTriangleCount(); + + WriteBinary<4, int>(header.revision, stream); + WriteBinary<4, int>(header.version, stream); + WriteBinary<4, int>(header.totalTriangles, stream); + for (int i = 0; i < 10; ++i) + WriteBinary<4, int>(header.reserved[i], stream); + + for (const ModelTriangle& triangle : mesh->GetTriangles()) + { + OldModelTriangleV3 t; + + t.used = true; + + t.p1 = triangle.p1; + t.p2 = triangle.p2; + t.p3 = triangle.p3; + + t.material.ambient = triangle.ambient; + t.material.diffuse = triangle.diffuse; + t.material.specular = triangle.specular; + strncpy(t.texName, triangle.tex1Name.c_str(), 20); + t.min = 0.0f; + t.max = 1000000.0f; + t.state = ConvertToOldState(triangle); + + int no = 0; + if (triangle.variableTex2) + no = 1; + else + std::sscanf(triangle.tex2Name.c_str(), "dirty%d.png", &no); + + t.texNum2 = no; + + + WriteBinary<1, char>(t.used, stream); + WriteBinary<1, char>(t.selected, stream); + + /* padding */ WriteBinary<2, unsigned int>(0, stream); + + WriteBinaryVertexTex2(t.p1, stream); + WriteBinaryVertexTex2(t.p2, stream); + WriteBinaryVertexTex2(t.p3, stream); + + WriteBinaryMaterial(t.material, stream); + stream.write(t.texName, 20); + WriteBinaryFloat(t.min, stream); + WriteBinaryFloat(t.max, stream); + WriteBinary<4, long>(t.state, stream); + WriteBinary<2, short>(t.texNum2, stream); + + WriteBinary<2, short>(t.reserved2, stream); + WriteBinary<2, short>(t.reserved3, stream); + WriteBinary<2, short>(t.reserved4, stream); + } +} + +int ModelOutput::ConvertToOldState(const ModelTriangle& triangle) +{ + int state = 0; + + switch (triangle.transparentMode) + { + case ModelTransparentMode::None: + break; + + case ModelTransparentMode::AlphaChannel: + state |= static_cast(ModelRenderState::Alpha); + break; + + case ModelTransparentMode::MapBlackToAlpha: + state |= static_cast(ModelRenderState::TTextureBlack); + break; + + case ModelTransparentMode::MapWhiteToAlpha: + state |= static_cast(ModelRenderState::TTextureWhite); + break; + } + + switch (triangle.specialMark) + { + case ModelSpecialMark::None: + break; + + case ModelSpecialMark::Part1: + state |= static_cast(ModelRenderState::Part1); + break; + + case ModelSpecialMark::Part2: + state |= static_cast(ModelRenderState::Part2); + break; + + case ModelSpecialMark::Part3: + state |= static_cast(ModelRenderState::Part3); + break; + } + + if (triangle.doubleSided) + state |= static_cast(ModelRenderState::TwoFace); + + return state; +} + +void ModelOutput::WriteBinaryVertexTex2(VertexTex2 vertex, std::ostream &stream) +{ + WriteBinaryFloat(vertex.coord.x, stream); + WriteBinaryFloat(vertex.coord.y, stream); + WriteBinaryFloat(vertex.coord.z, stream); + WriteBinaryFloat(vertex.normal.x, stream); + WriteBinaryFloat(vertex.normal.y, stream); + WriteBinaryFloat(vertex.normal.z, stream); + WriteBinaryFloat(vertex.texCoord.x, stream); + WriteBinaryFloat(vertex.texCoord.y, stream); + WriteBinaryFloat(vertex.texCoord2.x, stream); + WriteBinaryFloat(vertex.texCoord2.y, stream); +} + +void ModelOutput::WriteBinaryMaterial(const Material& material, std::ostream &stream) +{ + WriteBinaryFloat(material.diffuse.r, stream); + WriteBinaryFloat(material.diffuse.g, stream); + WriteBinaryFloat(material.diffuse.b, stream); + WriteBinaryFloat(material.diffuse.a, stream); + + WriteBinaryFloat(material.ambient.r, stream); + WriteBinaryFloat(material.ambient.g, stream); + WriteBinaryFloat(material.ambient.b, stream); + WriteBinaryFloat(material.ambient.a, stream); + + WriteBinaryFloat(material.specular.r, stream); + WriteBinaryFloat(material.specular.g, stream); + WriteBinaryFloat(material.specular.b, stream); + WriteBinaryFloat(material.specular.a, stream); + + /* emissive.r */ WriteBinaryFloat(0.0f, stream); + /* emissive.g */ WriteBinaryFloat(0.0f, stream); + /* emissive.b */ WriteBinaryFloat(0.0f, stream); + /* emissive.a */ WriteBinaryFloat(0.0f, stream); + + /* power */ WriteBinaryFloat(0.0f, stream); +} + +void ModelOutput::WriteTextVertexTex2(const VertexTex2& vertex, std::ostream &stream) +{ + stream << "c " << vertex.coord.x << " " << vertex.coord.y << " " << vertex.coord.z; + stream << " n " << vertex.normal.x << " " << vertex.normal.y << " " << vertex.normal.z; + stream << " t1 " << vertex.texCoord.x << " " << vertex.texCoord.y; + stream << " t2 " << vertex.texCoord2.x << " " << vertex.texCoord2.y; + stream << std::endl; +} + +void ModelOutput::WriteTextMaterial(const Material& material, std::ostream &stream) +{ + stream << "dif " << material.diffuse.r + << " " << material.diffuse.g + << " " << material.diffuse.b + << " " << material.diffuse.a; + + stream << " amb " << material.ambient.r + << " " << material.ambient.g + << " " << material.ambient.b + << " " << material.ambient.a; + + stream << " spc " << material.specular.r + << " " << material.specular.g << " " + << material.specular.b << " " + << material.specular.a; + + stream << std::endl; +} + + +} // namespace Gfx diff --git a/src/graphics/model/model_output.h b/src/graphics/model/model_output.h new file mode 100644 index 00000000..78fb8ec6 --- /dev/null +++ b/src/graphics/model/model_output.h @@ -0,0 +1,21 @@ +#pragma once + +#include "graphics/model/model_format.h" + +#include +#include + +namespace Gfx { + +class CModel; + +namespace ModelOutput +{ + //! Writes the given \a model to \a stream using \a format + /** + * @throws CModelIOException on read/write error + */ + void Write(const CModel& model, std::ostream& stream, ModelFormat format); +} + +} // namespace Gfx diff --git a/src/graphics/model/model_shadow_spot.h b/src/graphics/model/model_shadow_spot.h new file mode 100644 index 00000000..05245026 --- /dev/null +++ b/src/graphics/model/model_shadow_spot.h @@ -0,0 +1,15 @@ +#pragma once + +namespace Gfx { + +/** + * \struct ModelShadowSpot + * \brief Shadow spot data as saved in model file + */ +struct ModelShadowSpot +{ + float radius = 0.0f; + float intensity = 0.0f; +}; + +} // namespace Gfx diff --git a/src/graphics/model/model_triangle.h b/src/graphics/model/model_triangle.h new file mode 100644 index 00000000..fc38a9ba --- /dev/null +++ b/src/graphics/model/model_triangle.h @@ -0,0 +1,68 @@ +#pragma once + +#include "graphics/core/color.h" +#include "graphics/core/vertex.h" + +namespace Gfx { + +/** + * \enum ModelSpecialMark + * \brief Special marking for some models + * + * TODO: refactor/remove in the future + */ +enum class ModelSpecialMark +{ + None, + Part1, + Part2, + Part3 +}; + +/** + * \enum ModelTransparentMode + * \brief Describes how to deal with texture transparency + * + * TODO: get rid of it in the future (use only alpha channel) + */ +enum class ModelTransparentMode +{ + None, //!< no transparency + AlphaChannel, //!< use alpha channel + MapBlackToAlpha, //!< map black color to alpha + MapWhiteToAlpha //!< map white color to alpha +}; + +/** + * \struct ModelTriangle + * \brief A single triangle in mesh as saved in model file + */ +struct ModelTriangle +{ + //! 1st vertex + VertexTex2 p1; + //! 2nd vertex + VertexTex2 p2; + //! 3rd vertex + VertexTex2 p3; + //! Diffuse color + Color diffuse; + //! Ambient color + Color ambient; + //! Specular color + Color specular; + //! Name of 1st texture + std::string tex1Name; + //! Name of 2nd texture + std::string tex2Name; + //! If true, 2nd texture will be taken from current engine setting + bool variableTex2 = false; + //! Whether to render as double-sided surface + bool doubleSided = false; + //! How to deal with texture transparency + ModelTransparentMode transparentMode = ModelTransparentMode::None; + //! Special marking + ModelSpecialMark specialMark = ModelSpecialMark::None; +}; + +} // namespace Gfx diff --git a/src/object/auto/autoportico.cpp b/src/object/auto/autoportico.cpp index 1d955e2b..4660245f 100644 --- a/src/object/auto/autoportico.cpp +++ b/src/object/auto/autoportico.cpp @@ -412,11 +412,11 @@ void CAutoPortico::UpdateTrackMapping(float left, float right) rank = m_object->GetObjectRank(0); m_engine->TrackTextureMapping(rank, mat, Gfx::ENG_RSTATE_PART1, "objects/lemt.png", "", - Gfx::LOD_Constant, Gfx::ENG_TEX_MAPPING_X, + Gfx::ENG_TEX_MAPPING_X, right, 8.0f, 8.0f, 192.0f, 256.0f); m_engine->TrackTextureMapping(rank, mat, Gfx::ENG_RSTATE_PART2, "objects/lemt.png", "", - Gfx::LOD_Constant, Gfx::ENG_TEX_MAPPING_X, + Gfx::ENG_TEX_MAPPING_X, left, 8.0f, 8.0f, 192.0f, 256.0f); } diff --git a/src/object/motion/motion.h b/src/object/motion/motion.h index 55df1898..55aef5f8 100644 --- a/src/object/motion/motion.h +++ b/src/object/motion/motion.h @@ -55,7 +55,7 @@ public: void SetBrain(CBrain* brain); virtual void DeleteObject(bool bAll=false) = 0; - virtual void Create(Math::Vector pos, float angle, ObjectType type, float power, Gfx::CModelManager* modelManager) = 0; + virtual void Create(Math::Vector pos, float angle, ObjectType type, float power, Gfx::COldModelManager* modelManager) = 0; virtual bool EventProcess(const Event &event); virtual Error SetAction(int action, float time=0.2f); virtual int GetAction(); diff --git a/src/object/motion/motionant.cpp b/src/object/motion/motionant.cpp index 68b55024..5d7529a0 100644 --- a/src/object/motion/motionant.cpp +++ b/src/object/motion/motionant.cpp @@ -22,7 +22,7 @@ #include "app/app.h" -#include "graphics/engine/modelmanager.h" +#include "graphics/engine/oldmodelmanager.h" #include "graphics/engine/particle.h" #include "physics/physics.h" @@ -69,7 +69,7 @@ void CMotionAnt::DeleteObject(bool bAll) // Creates a vehicle poses some rolling on the floor. void CMotionAnt::Create(Math::Vector pos, float angle, ObjectType type, - float power, Gfx::CModelManager* modelManager) + float power, Gfx::COldModelManager* modelManager) { int rank; diff --git a/src/object/motion/motionant.h b/src/object/motion/motionant.h index f028df88..46b5afad 100644 --- a/src/object/motion/motionant.h +++ b/src/object/motion/motionant.h @@ -53,7 +53,7 @@ public: ~CMotionAnt(); void DeleteObject(bool bAll=false); - void Create(Math::Vector pos, float angle, ObjectType type, float power, Gfx::CModelManager* modelManager); + void Create(Math::Vector pos, float angle, ObjectType type, float power, Gfx::COldModelManager* modelManager); bool EventProcess(const Event &event); protected: diff --git a/src/object/motion/motionbee.cpp b/src/object/motion/motionbee.cpp index b4157475..658237c4 100644 --- a/src/object/motion/motionbee.cpp +++ b/src/object/motion/motionbee.cpp @@ -22,7 +22,7 @@ #include "app/app.h" -#include "graphics/engine/modelmanager.h" +#include "graphics/engine/oldmodelmanager.h" #include "physics/physics.h" @@ -67,7 +67,7 @@ void CMotionBee::DeleteObject(bool bAll) // Creates a vehicle traveling any lands on the ground. void CMotionBee::Create(Math::Vector pos, float angle, ObjectType type, - float power, Gfx::CModelManager* modelManager) + float power, Gfx::COldModelManager* modelManager) { int rank; diff --git a/src/object/motion/motionbee.h b/src/object/motion/motionbee.h index f15dc947..8484a4c7 100644 --- a/src/object/motion/motionbee.h +++ b/src/object/motion/motionbee.h @@ -47,7 +47,7 @@ public: ~CMotionBee(); void DeleteObject(bool bAll=false); - void Create(Math::Vector pos, float angle, ObjectType type, float power, Gfx::CModelManager* modelManager); + void Create(Math::Vector pos, float angle, ObjectType type, float power, Gfx::COldModelManager* modelManager); bool EventProcess(const Event &event); protected: diff --git a/src/object/motion/motiondummy.cpp b/src/object/motion/motiondummy.cpp index 8874d9a8..9f2747ce 100644 --- a/src/object/motion/motiondummy.cpp +++ b/src/object/motion/motiondummy.cpp @@ -20,7 +20,7 @@ #include "object/motion/motiondummy.h" #include "physics/physics.h" -#include "graphics/engine/modelmanager.h" +#include "graphics/engine/oldmodelmanager.h" #include #include @@ -48,7 +48,7 @@ void CMotionDummy::DeleteObject(bool bAll) // Creates a Dummy traveling any lands on the ground. void CMotionDummy::Create(Math::Vector pos, float angle, ObjectType type, - float power, Gfx::CModelManager*) + float power, Gfx::COldModelManager*) { m_object->SetType(type); diff --git a/src/object/motion/motiondummy.h b/src/object/motion/motiondummy.h index b6227e84..7c449d27 100644 --- a/src/object/motion/motiondummy.h +++ b/src/object/motion/motiondummy.h @@ -31,6 +31,6 @@ public: ~CMotionDummy(); void DeleteObject(bool bAll=false); - void Create(Math::Vector pos, float angle, ObjectType type, float power, Gfx::CModelManager* modelManager); + void Create(Math::Vector pos, float angle, ObjectType type, float power, Gfx::COldModelManager* modelManager); }; diff --git a/src/object/motion/motionhuman.cpp b/src/object/motion/motionhuman.cpp index fb07734e..cf9f2283 100644 --- a/src/object/motion/motionhuman.cpp +++ b/src/object/motion/motionhuman.cpp @@ -22,7 +22,7 @@ #include "app/app.h" -#include "graphics/engine/modelmanager.h" +#include "graphics/engine/oldmodelmanager.h" #include "graphics/engine/terrain.h" #include "graphics/engine/water.h" @@ -97,7 +97,7 @@ Error CMotionHuman::SetAction(int action, float time) // Creates cosmonaut on the ground. void CMotionHuman::Create(Math::Vector pos, float angle, ObjectType type, - float power, Gfx::CModelManager* modelManager) + float power, Gfx::COldModelManager* modelManager) { char filename[100]; int rank, option, face, glasses; diff --git a/src/object/motion/motionhuman.h b/src/object/motion/motionhuman.h index 87a05606..1bbec1ec 100644 --- a/src/object/motion/motionhuman.h +++ b/src/object/motion/motionhuman.h @@ -65,7 +65,7 @@ public: ~CMotionHuman(); void DeleteObject(bool bAll=false); - void Create(Math::Vector pos, float angle, ObjectType type, float power, Gfx::CModelManager* modelManager); + void Create(Math::Vector pos, float angle, ObjectType type, float power, Gfx::COldModelManager* modelManager); bool EventProcess(const Event &event); Error SetAction(int action, float time=0.2f); diff --git a/src/object/motion/motionmother.cpp b/src/object/motion/motionmother.cpp index 7372e858..b3b5766e 100644 --- a/src/object/motion/motionmother.cpp +++ b/src/object/motion/motionmother.cpp @@ -22,7 +22,7 @@ #include "app/app.h" -#include "graphics/engine/modelmanager.h" +#include "graphics/engine/oldmodelmanager.h" #include "physics/physics.h" @@ -68,7 +68,7 @@ void CMotionMother::DeleteObject(bool bAll) // Creates a vehicle traveling any lands on the ground. void CMotionMother::Create(Math::Vector pos, float angle, ObjectType type, - float power, Gfx::CModelManager* modelManager) + float power, Gfx::COldModelManager* modelManager) { int rank; diff --git a/src/object/motion/motionmother.h b/src/object/motion/motionmother.h index d3ead13c..90684339 100644 --- a/src/object/motion/motionmother.h +++ b/src/object/motion/motionmother.h @@ -33,7 +33,7 @@ public: ~CMotionMother(); void DeleteObject(bool bAll=false); - void Create(Math::Vector pos, float angle, ObjectType type, float power, Gfx::CModelManager* modelManager); + void Create(Math::Vector pos, float angle, ObjectType type, float power, Gfx::COldModelManager* modelManager); bool EventProcess(const Event &event); protected: diff --git a/src/object/motion/motionspider.cpp b/src/object/motion/motionspider.cpp index d028a4b5..3abbd638 100644 --- a/src/object/motion/motionspider.cpp +++ b/src/object/motion/motionspider.cpp @@ -22,7 +22,7 @@ #include "app/app.h" -#include "graphics/engine/modelmanager.h" +#include "graphics/engine/oldmodelmanager.h" #include "graphics/engine/particle.h" #include "physics/physics.h" @@ -69,7 +69,7 @@ void CMotionSpider::DeleteObject(bool bAll) // Creates a vehicle traveling any lands on the ground. void CMotionSpider::Create(Math::Vector pos, float angle, ObjectType type, - float power, Gfx::CModelManager* modelManager) + float power, Gfx::COldModelManager* modelManager) { int rank, i, j, parent; char name[50]; diff --git a/src/object/motion/motionspider.h b/src/object/motion/motionspider.h index 78337800..71de1b06 100644 --- a/src/object/motion/motionspider.h +++ b/src/object/motion/motionspider.h @@ -51,7 +51,7 @@ public: ~CMotionSpider(); void DeleteObject(bool bAll=false); - void Create(Math::Vector pos, float angle, ObjectType type, float power, Gfx::CModelManager* modelManager); + void Create(Math::Vector pos, float angle, ObjectType type, float power, Gfx::COldModelManager* modelManager); bool EventProcess(const Event &event); protected: diff --git a/src/object/motion/motiontoto.cpp b/src/object/motion/motiontoto.cpp index 1e97e2c9..c478b2ec 100644 --- a/src/object/motion/motiontoto.cpp +++ b/src/object/motion/motiontoto.cpp @@ -24,7 +24,7 @@ #include "math/geometry.h" -#include "graphics/engine/modelmanager.h" +#include "graphics/engine/oldmodelmanager.h" #include "graphics/engine/terrain.h" #include "graphics/engine/water.h" @@ -79,7 +79,7 @@ void CMotionToto::DeleteObject(bool bAll) // Creates a vehicle traveling any lands on the ground. void CMotionToto::Create(Math::Vector pos, float angle, ObjectType type, - float power, Gfx::CModelManager* modelManager) + float power, Gfx::COldModelManager* modelManager) { int rank; diff --git a/src/object/motion/motiontoto.h b/src/object/motion/motiontoto.h index e691cad7..cf2f9e42 100644 --- a/src/object/motion/motiontoto.h +++ b/src/object/motion/motiontoto.h @@ -42,7 +42,7 @@ public: ~CMotionToto(); void DeleteObject(bool bAll=false); - void Create(Math::Vector pos, float angle, ObjectType type, float power, Gfx::CModelManager* modelManager); + void Create(Math::Vector pos, float angle, ObjectType type, float power, Gfx::COldModelManager* modelManager); bool EventProcess(const Event &event); Error SetAction(int action, float time=0.2f); void SetLinkType(ObjectType type); diff --git a/src/object/motion/motionvehicle.cpp b/src/object/motion/motionvehicle.cpp index 18755242..8b9392d4 100644 --- a/src/object/motion/motionvehicle.cpp +++ b/src/object/motion/motionvehicle.cpp @@ -22,7 +22,7 @@ #include "app/app.h" -#include "graphics/engine/modelmanager.h" +#include "graphics/engine/oldmodelmanager.h" #include "graphics/engine/particle.h" #include "graphics/engine/terrain.h" @@ -93,7 +93,7 @@ void CMotionVehicle::DeleteObject(bool bAll) // Creates a vehicle traveling any lands on the ground. void CMotionVehicle::Create(Math::Vector pos, float angle, ObjectType type, - float power, Gfx::CModelManager* modelManager) + float power, Gfx::COldModelManager* modelManager) { int rank, i, j, parent; Gfx::Color color; @@ -1881,25 +1881,22 @@ void CMotionVehicle::UpdateTrackMapping(float left, float right, ObjectType type if (type == OBJECT_MOBILEdr) { m_engine->TrackTextureMapping(rRank, mat, Gfx::ENG_RSTATE_PART1, "objects/drawer.png", "", - Gfx::LOD_Constant, Gfx::ENG_TEX_MAPPING_X, + Gfx::ENG_TEX_MAPPING_X, right, 1.0f, 8.0f, 192.0f, 256.0f); m_engine->TrackTextureMapping(lRank, mat, Gfx::ENG_RSTATE_PART2, "objects/drawer.png", "", - Gfx::LOD_Constant, Gfx::ENG_TEX_MAPPING_X, + Gfx::ENG_TEX_MAPPING_X, left, 1.0f, 8.0f, 192.0f, 256.0f); } else { - for (int i = 0; i < 2; i++) - { m_engine->TrackTextureMapping(rRank, mat, Gfx::ENG_RSTATE_PART1, "objects/lemt.png", "", - (i == 0) ? Gfx::LOD_High : Gfx::LOD_Medium, Gfx::ENG_TEX_MAPPING_X, - right, 1.0f, 8.0f, 192.0f, 256.0f); + Gfx::ENG_TEX_MAPPING_X, + right, 1.0f, 8.0f, 192.0f, 256.0f); - m_engine->TrackTextureMapping(lRank, mat, Gfx::ENG_RSTATE_PART2, "objects/lemt.png", "", - (i == 0) ? Gfx::LOD_High : Gfx::LOD_Medium, Gfx::ENG_TEX_MAPPING_X, - left, 1.0f, 8.0f, 192.0f, 256.0f); - } + m_engine->TrackTextureMapping(lRank, mat, Gfx::ENG_RSTATE_PART2, "lemt.png", "", + Gfx::ENG_TEX_MAPPING_X, + left, 1.0f, 8.0f, 192.0f, 256.0f); } } diff --git a/src/object/motion/motionvehicle.h b/src/object/motion/motionvehicle.h index 41e69ad8..deaaaacb 100644 --- a/src/object/motion/motionvehicle.h +++ b/src/object/motion/motionvehicle.h @@ -33,7 +33,7 @@ public: ~CMotionVehicle(); void DeleteObject(bool bAll=false); - void Create(Math::Vector pos, float angle, ObjectType type, float power, Gfx::CModelManager* modelManager); + void Create(Math::Vector pos, float angle, ObjectType type, float power, Gfx::COldModelManager* modelManager); bool EventProcess(const Event &event); bool GetTraceDown(); diff --git a/src/object/motion/motionworm.cpp b/src/object/motion/motionworm.cpp index 11c802e2..878bf0d3 100644 --- a/src/object/motion/motionworm.cpp +++ b/src/object/motion/motionworm.cpp @@ -22,7 +22,7 @@ #include "app/app.h" -#include "graphics/engine/modelmanager.h" +#include "graphics/engine/oldmodelmanager.h" #include "graphics/engine/particle.h" #include "graphics/engine/terrain.h" @@ -81,7 +81,7 @@ void CMotionWorm::DeleteObject(bool bAll) // Creates a vehicle traveling any lands on the ground. void CMotionWorm::Create(Math::Vector pos, float angle, ObjectType type, - float power, Gfx::CModelManager* modelManager) + float power, Gfx::COldModelManager* modelManager) { int rank, i; float px; diff --git a/src/object/motion/motionworm.h b/src/object/motion/motionworm.h index 0eb9a9f0..8423bcf8 100644 --- a/src/object/motion/motionworm.h +++ b/src/object/motion/motionworm.h @@ -33,7 +33,7 @@ public: ~CMotionWorm(); void DeleteObject(bool bAll=false); - void Create(Math::Vector pos, float angle, ObjectType type, float power, Gfx::CModelManager* modelManager); + void Create(Math::Vector pos, float angle, ObjectType type, float power, Gfx::COldModelManager* modelManager); bool EventProcess(const Event &event); bool SetParam(int rank, float value); diff --git a/src/object/object_factory.cpp b/src/object/object_factory.cpp index 65143bb2..20151a29 100644 --- a/src/object/object_factory.cpp +++ b/src/object/object_factory.cpp @@ -20,7 +20,7 @@ #include "object/object_factory.h" #include "graphics/engine/engine.h" -#include "graphics/engine/modelmanager.h" +#include "graphics/engine/oldmodelmanager.h" #include "graphics/engine/terrain.h" #include "graphics/engine/lightning.h" @@ -71,7 +71,7 @@ using COldObjectUPtr = std::unique_ptr; CObjectFactory::CObjectFactory(Gfx::CEngine* engine, Gfx::CTerrain* terrain, - Gfx::CModelManager* modelManager, + Gfx::COldModelManager* modelManager, Gfx::CParticle* particle, CRobotMain* main) : m_engine(engine) diff --git a/src/object/object_factory.h b/src/object/object_factory.h index 0e3e6cd0..2e91a049 100644 --- a/src/object/object_factory.h +++ b/src/object/object_factory.h @@ -32,7 +32,7 @@ namespace Gfx { class CEngine; -class CModelManager; +class COldModelManager; class CParticle; class CTerrain; } // namespace Gfx @@ -49,7 +49,7 @@ class CObjectFactory public: CObjectFactory(Gfx::CEngine* engine, Gfx::CTerrain* terrain, - Gfx::CModelManager* modelManager, + Gfx::COldModelManager* modelManager, Gfx::CParticle* particle, CRobotMain* main); @@ -75,7 +75,7 @@ private: private: Gfx::CEngine* m_engine; Gfx::CTerrain* m_terrain; - Gfx::CModelManager* m_modelManager; + Gfx::COldModelManager* m_modelManager; Gfx::CParticle* m_particle; CRobotMain* m_main; }; diff --git a/src/object/object_manager.cpp b/src/object/object_manager.cpp index 4c6bd0d5..ae7bba19 100644 --- a/src/object/object_manager.cpp +++ b/src/object/object_manager.cpp @@ -38,7 +38,7 @@ template<> CObjectManager* CSingleton::m_instance = nullptr; CObjectManager::CObjectManager(Gfx::CEngine* engine, Gfx::CTerrain* terrain, - Gfx::CModelManager* modelManager, + Gfx::COldModelManager* modelManager, Gfx::CParticle* particle, CRobotMain* main) : m_objectFactory(new CObjectFactory(engine, terrain, modelManager, particle, main)) diff --git a/src/object/object_manager.h b/src/object/object_manager.h index 0052d8ff..dfddfe46 100644 --- a/src/object/object_manager.h +++ b/src/object/object_manager.h @@ -37,7 +37,7 @@ namespace Gfx { class CEngine; -class CModelManager; +class COldModelManager; class CParticle; class CTerrain; } // namespace Gfx @@ -122,7 +122,7 @@ class CObjectManager : public CSingleton public: CObjectManager(Gfx::CEngine* engine, Gfx::CTerrain* terrain, - Gfx::CModelManager* modelManager, + Gfx::COldModelManager* modelManager, Gfx::CParticle* particle, CRobotMain* main); virtual ~CObjectManager(); diff --git a/src/object/old_object.cpp b/src/object/old_object.cpp index 2e277078..460a29e4 100644 --- a/src/object/old_object.cpp +++ b/src/object/old_object.cpp @@ -29,7 +29,6 @@ #include "graphics/engine/lightman.h" #include "graphics/engine/lightning.h" -#include "graphics/engine/modelmanager.h" #include "graphics/engine/particle.h" #include "graphics/engine/pyro_manager.h" #include "graphics/engine/terrain.h" @@ -2129,15 +2128,10 @@ void COldObject::UpdateEnergyMapping() float au = (s-i)/(b-a); float bu = s-b*(s-i)/(b-a); - Gfx::LODLevel lodLevels[3] = { Gfx::LOD_High, Gfx::LOD_Medium, Gfx::LOD_Low }; - - for (int j = 0; j < 3; j++) - { - m_engine->ChangeTextureMapping(m_objectPart[0].object, - mat, Gfx::ENG_RSTATE_PART3, "objects/lemt.png", "", - lodLevels[j], Gfx::ENG_TEX_MAPPING_1Y, - au, bu, 1.0f, 0.0f); - } + m_engine->ChangeTextureMapping(m_objectPart[0].object, + mat, Gfx::ENG_RSTATE_PART3, "objects/lemt.png", "", + Gfx::ENG_TEX_MAPPING_1Y, + au, bu, 1.0f, 0.0f); } diff --git a/src/object/robotmain.cpp b/src/object/robotmain.cpp index 827cd188..7e2c4213 100644 --- a/src/object/robotmain.cpp +++ b/src/object/robotmain.cpp @@ -44,7 +44,7 @@ #include "graphics/engine/engine.h" #include "graphics/engine/lightman.h" #include "graphics/engine/lightning.h" -#include "graphics/engine/modelmanager.h" +#include "graphics/engine/oldmodelmanager.h" #include "graphics/engine/particle.h" #include "graphics/engine/planet.h" #include "graphics/engine/pyro_manager.h" diff --git a/src/object/robotmain.h b/src/object/robotmain.h index 0968c80c..2b4be461 100644 --- a/src/object/robotmain.h +++ b/src/object/robotmain.h @@ -381,7 +381,7 @@ protected: Gfx::CCloud* m_cloud; Gfx::CLightning* m_lightning; Gfx::CPlanet* m_planet; - Gfx::CModelManager* m_modelManager; + Gfx::COldModelManager* m_modelManager; Gfx::CLightManager* m_lightMan; Gfx::CTerrain* m_terrain; Gfx::CCamera* m_camera; diff --git a/src/object/subclass/exchange_post.cpp b/src/object/subclass/exchange_post.cpp index 0dbd47c0..0d7bb551 100644 --- a/src/object/subclass/exchange_post.cpp +++ b/src/object/subclass/exchange_post.cpp @@ -21,7 +21,7 @@ #include "common/regex_utils.h" -#include "graphics/engine/modelmanager.h" +#include "graphics/engine/oldmodelmanager.h" #include "object/object_create_params.h" #include "object/level/parserline.h" @@ -46,7 +46,7 @@ CExchangePost::CExchangePost(int id) std::unique_ptr CExchangePost::Create( const ObjectCreateParams& params, - Gfx::CModelManager* modelManager, + Gfx::COldModelManager* modelManager, Gfx::CEngine* engine) { std::unique_ptr obj{new CExchangePost(params.id)}; diff --git a/src/object/subclass/exchange_post.h b/src/object/subclass/exchange_post.h index 4ed622b4..b3713442 100644 --- a/src/object/subclass/exchange_post.h +++ b/src/object/subclass/exchange_post.h @@ -37,7 +37,7 @@ struct ExchangePostInfo struct ObjectCreateParams; namespace Gfx { -class CModelManager; +class COldModelManager; class CEngine; } @@ -48,7 +48,7 @@ public: static std::unique_ptr Create( const ObjectCreateParams& params, - Gfx::CModelManager* modelManager, + Gfx::COldModelManager* modelManager, Gfx::CEngine* engine); static int GetMaximumInfoListSize(); diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt index da590d81..c588f7b7 100644 --- a/src/tools/CMakeLists.txt +++ b/src/tools/CMakeLists.txt @@ -1,15 +1,15 @@ set(CONVERT_MODEL_SOURCES -../common/logger.cpp -../common/stringutils.cpp -../graphics/engine/modelfile.cpp -convert_model.cpp + ../common/logger.cpp + ../graphics/model/model.cpp + ../graphics/model/model_mesh.cpp + ../graphics/model/model_input.cpp + ../graphics/model/model_output.cpp + convert_model.cpp ) include_directories(. ..) include_directories(SYSTEM ${SDL_INCLUDE_DIR}) -add_definitions(-DMODELFILE_NO_ENGINE) - add_executable(convert_model ${CONVERT_MODEL_SOURCES}) diff --git a/src/tools/convert_model.cpp b/src/tools/convert_model.cpp index 44afecd1..96c2dd65 100644 --- a/src/tools/convert_model.cpp +++ b/src/tools/convert_model.cpp @@ -17,11 +17,17 @@ * along with this program. If not, see http://gnu.org/licenses */ #include "common/logger.h" -#include "graphics/engine/modelfile.h" + +#include "graphics/model/model_input.h" +#include "graphics/model/model_output.h" +#include "graphics/model/model_io_exception.h" #include +#include #include +using namespace Gfx; + bool EndsWith(std::string const &fullString, std::string const &ending) { @@ -151,18 +157,6 @@ bool ParseArgs(int argc, char *argv[]) return true; } -std::ostream& operator<<(std::ostream& stream, Gfx::LODLevel lodLevel) -{ - switch (lodLevel) - { - case Gfx::LOD_Constant: stream << "constant"; break; - case Gfx::LOD_High: stream << "high"; break; - case Gfx::LOD_Medium: stream << "medium"; break; - case Gfx::LOD_Low: stream << "low"; break; - } - return stream; -} - template void PrintStats(const std::map& stats, int total) { @@ -172,6 +166,57 @@ void PrintStats(const std::map& stats, int total) } } +void DumpInfo(const CModel& model) +{ + const CModelMesh* mesh = model.GetMesh("main"); + if (mesh == nullptr) + { + std::cerr << "Main mesh not found!" << std::endl; + return; + } + + Math::Vector bboxMin( Math::HUGE_NUM, Math::HUGE_NUM, Math::HUGE_NUM); + Math::Vector bboxMax(-Math::HUGE_NUM, -Math::HUGE_NUM, -Math::HUGE_NUM); + + std::map texs1, texs2; + std::map states; + int variableTexs2 = 0; + + for (const ModelTriangle& t : mesh->GetTriangles()) + { + bboxMin.x = Math::Min(t.p1.coord.x, t.p2.coord.x, t.p3.coord.x, bboxMin.x); + bboxMin.y = Math::Min(t.p1.coord.y, t.p2.coord.y, t.p3.coord.y, bboxMin.y); + bboxMin.z = Math::Min(t.p1.coord.z, t.p2.coord.z, t.p3.coord.z, bboxMin.z); + + bboxMax.x = Math::Max(t.p1.coord.x, t.p2.coord.x, t.p3.coord.x, bboxMax.x); + bboxMax.y = Math::Max(t.p1.coord.y, t.p2.coord.y, t.p3.coord.y, bboxMax.y); + bboxMax.z = Math::Max(t.p1.coord.z, t.p2.coord.z, t.p3.coord.z, bboxMax.z); + + texs1[t.tex1Name] += 1; + if (! t.tex2Name.empty()) + texs2[t.tex2Name] += 1; + if (t.variableTex2) + variableTexs2 += 1; + } + + int total = mesh->GetTriangleCount(); + + std::cerr << "---- Info ----" << std::endl; + std::cerr << "Total triangles: " << total; + std::cerr << std::endl; + std::cerr << "Bounding box:" << std::endl; + std::cerr << " bboxMin: [" << bboxMin.x << ", " << bboxMin.y << ", " << bboxMin.z << "]" << std::endl; + std::cerr << " bboxMax: [" << bboxMax.x << ", " << bboxMax.y << ", " << bboxMax.z << "]" << std::endl; + std::cerr << std::endl; + std::cerr << "Textures:" << std::endl; + std::cerr << " tex1:" << std::endl; + PrintStats(texs1, total); + std::cerr << " tex2:" << std::endl; + PrintStats(texs2, total); + std::cerr << " variable tex2: " << variableTexs2 << " / " << total << std::endl; + std::cerr << std::endl; +} + int main(int argc, char *argv[]) { CLogger logger; @@ -186,112 +231,71 @@ int main(int argc, char *argv[]) if (ARGS.usage) return 0; - Gfx::CModelFile model; - - bool ok = true; + ModelFormat inputFormat = ModelFormat::Old; if (ARGS.inputFormat == "old") - { - ok = model.ReadModel(ARGS.inputFile); - } + inputFormat = ModelFormat::Old; else if (ARGS.inputFormat == "new_bin") - { - ok = model.ReadBinaryModel(ARGS.inputFile); - } + inputFormat = ModelFormat::Binary; else if (ARGS.inputFormat == "new_txt") - { - ok = model.ReadTextModel(ARGS.inputFile); - } + inputFormat = ModelFormat::Text; else { - std::cerr << "Invalid input format" << std::endl; + std::cerr << "Invalid input format: " << ARGS.inputFormat << std::endl; return 1; } - if (!ok) + CModel model; + + try { - std::cerr << "Reading input model failed" << std::endl; + std::ifstream stream; + stream.open(ARGS.inputFile, std::ios_base::in | std::ios_base::binary); + if (!stream.good()) + throw CModelIOException(std::string("Could not open file: ") + ARGS.inputFile); + + model = ModelInput::Read(stream, inputFormat); + } + catch (const CModelIOException& e) + { + std::cerr << "Reading input model failed with error:" << std::endl; + std::cerr << e.what() << std::endl; return 1; } if (ARGS.dumpInfo) { - const std::vector& triangles = model.GetTriangles(); - - Math::Vector bboxMin( Math::HUGE_NUM, Math::HUGE_NUM, Math::HUGE_NUM); - Math::Vector bboxMax(-Math::HUGE_NUM, -Math::HUGE_NUM, -Math::HUGE_NUM); - - std::map texs1, texs2; - std::map states; - std::map lodLevels; - int variableTexs2 = 0; - - for (int i = 0; i < static_cast( triangles.size() ); ++i) - { - const Gfx::ModelTriangle& t = triangles[i]; - - bboxMin.x = Math::Min(t.p1.coord.x, t.p2.coord.x, t.p3.coord.x, bboxMin.x); - bboxMin.y = Math::Min(t.p1.coord.y, t.p2.coord.y, t.p3.coord.y, bboxMin.y); - bboxMin.z = Math::Min(t.p1.coord.z, t.p2.coord.z, t.p3.coord.z, bboxMin.z); - - bboxMax.x = Math::Max(t.p1.coord.x, t.p2.coord.x, t.p3.coord.x, bboxMax.x); - bboxMax.y = Math::Max(t.p1.coord.y, t.p2.coord.y, t.p3.coord.y, bboxMax.y); - bboxMax.z = Math::Max(t.p1.coord.z, t.p2.coord.z, t.p3.coord.z, bboxMax.z); - - texs1[t.tex1Name] += 1; - if (! t.tex2Name.empty()) - texs2[t.tex2Name] += 1; - if (t.variableTex2) - variableTexs2 += 1; - states[t.state] += 1; - - lodLevels[t.lodLevel] += 1; - } - - std::cerr << "---- Info ----" << std::endl; - std::cerr << "Total triangles: " << triangles.size(); - std::cerr << std::endl; - std::cerr << "Bounding box:" << std::endl; - std::cerr << " bboxMin: [" << bboxMin.x << ", " << bboxMin.y << ", " << bboxMin.z << "]" << std::endl; - std::cerr << " bboxMax: [" << bboxMax.x << ", " << bboxMax.y << ", " << bboxMax.z << "]" << std::endl; - std::cerr << std::endl; - std::cerr << "Textures:" << std::endl; - std::cerr << " tex1:" << std::endl; - PrintStats(texs1, triangles.size()); - std::cerr << " tex2:" << std::endl; - PrintStats(texs2, triangles.size()); - std::cerr << " variable tex2: " << variableTexs2 << " / " << triangles.size() << std::endl; - std::cerr << std::endl; - std::cerr << "States:" << std::endl; - PrintStats(states, triangles.size()); - std::cerr << std::endl; - std::cerr << "LOD:" << std::endl; - PrintStats(lodLevels, triangles.size()); + DumpInfo(model); return 0; } + ModelFormat outputFormat = ModelFormat::Old; + if (ARGS.outputFormat == "old") - { - ok = model.WriteModel(ARGS.outputFile); - } + outputFormat = ModelFormat::Old; else if (ARGS.outputFormat == "new_bin") - { - ok = model.WriteBinaryModel(ARGS.outputFile); - } + outputFormat = ModelFormat::Binary; else if (ARGS.outputFormat == "new_txt") - { - ok = model.WriteTextModel(ARGS.outputFile); - } + outputFormat = ModelFormat::Text; else { - std::cerr << "Invalid output format" << std::endl; + std::cerr << "Invalid output format: " << ARGS.outputFormat << std::endl; return 1; } - if (!ok) + try { - std::cerr << "Writing output model failed" << std::endl; + std::ofstream stream; + stream.open(ARGS.outputFile, std::ios_base::out | std::ios_base::binary); + if (!stream.good()) + throw CModelIOException(std::string("Could not open file: ") + ARGS.inputFile); + ModelOutput::Write(model, stream, outputFormat); + } + catch (const CModelIOException& e) + { + std::cerr << "Writing output model failed with error:" << std::endl; + std::cerr << e.what() << std::endl; return 1; }