From 920a4c3fc8a61b54a49b3a7a5a748eb7f3f45067 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Kapu=C5=9Bci=C5=84ski?= Date: Fri, 11 Mar 2022 21:06:40 +0100 Subject: [PATCH] Split model I/O to separate files based on model format and removed new binary format --- src/CMakeLists.txt | 4 + src/graphics/model/model_format.h | 1 - src/graphics/model/model_input.cpp | 862 +------------------------- src/graphics/model/model_io_structs.h | 21 + src/graphics/model/model_mod.cpp | 371 +++++++++++ src/graphics/model/model_mod.h | 36 ++ src/graphics/model/model_txt.cpp | 425 +++++++++++++ src/graphics/model/model_txt.h | 36 ++ 8 files changed, 897 insertions(+), 859 deletions(-) create mode 100644 src/graphics/model/model_mod.cpp create mode 100644 src/graphics/model/model_mod.h create mode 100644 src/graphics/model/model_txt.cpp create mode 100644 src/graphics/model/model_txt.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f055ed4f..8a25f8c0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -138,10 +138,14 @@ add_library(colobotbase STATIC graphics/model/model_manager.h graphics/model/model_mesh.cpp graphics/model/model_mesh.h + graphics/model/model_mod.cpp + graphics/model/model_mod.h graphics/model/model_output.cpp graphics/model/model_output.h graphics/model/model_shadow_spot.h graphics/model/model_triangle.h + graphics/model/model_txt.cpp + graphics/model/model_txt.h graphics/opengl/gl33device.cpp graphics/opengl/gl33device.h graphics/opengl/gl33renderers.cpp diff --git a/src/graphics/model/model_format.h b/src/graphics/model/model_format.h index 536e7b32..c046bc79 100644 --- a/src/graphics/model/model_format.h +++ b/src/graphics/model/model_format.h @@ -29,7 +29,6 @@ namespace Gfx enum class ModelFormat { Text, //!< new text format - Binary, //!< new binary format Old //!< old binary format, deprecated }; diff --git a/src/graphics/model/model_input.cpp b/src/graphics/model/model_input.cpp index c7bcbddb..2dc29792 100644 --- a/src/graphics/model/model_input.cpp +++ b/src/graphics/model/model_input.cpp @@ -19,85 +19,14 @@ #include "graphics/model/model_input.h" -#include "common/ioutils.h" -#include "common/logger.h" - -#include "common/resources/inputstream.h" +#include "graphics/model/model_mod.h" +#include "graphics/model/model_txt.h" #include "graphics/model/model_io_exception.h" -#include "graphics/model/model_io_structs.h" - -#include -#include - -#include -#include namespace Gfx { -//! Legacy material structure -struct LegacyMaterial -{ - //! Diffuse color - Color diffuse; - //! Ambient color - Color ambient; - //! Specular color - Color specular; - - bool operator==(const LegacyMaterial& mat) const - { - return diffuse == mat.diffuse && ambient == mat.ambient && specular == mat.specular; - } - - bool operator!=(const LegacyMaterial& mat) const - { - return !operator==(mat); - } -}; - -// 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); - ModelHeaderV3 ReadTextHeader(std::istream &stream); - CModelMesh ReadTextMesh(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); - - Vertex3D ReadBinaryVertex(std::istream& stream); - Vertex3D ReadBinaryVertexTex2(std::istream& stream); - LegacyMaterial ReadBinaryMaterial(std::istream& stream); - - std::string ReadLineString(std::istream& stream, const std::string& expectedPrefix); - void ReadValuePrefix(std::istream& stream, const std::string& expectedPrefix); - Vertex3D ParseVertexTex2(const std::string& text); - LegacyMaterial ParseMaterial(const std::string& text); - glm::vec3 ParseVector(const std::string& text); - ModelCrashSphere ParseCrashSphere(const std::string& text); - ModelShadowSpot ParseShadowSpot(const std::string& text); - Math::Sphere ParseCameraCollisionSphere(const std::string& text); - AlphaMode ParseTransparentMode(const std::string& text); - std::string ParseSpecialMark(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); @@ -108,16 +37,12 @@ CModel ModelInput::Read(std::istream &stream, ModelFormat format) { switch (format) { - case ModelFormat::Binary: - ReadBinaryModel(model, stream); - break; - case ModelFormat::Text: - ReadTextModel(model, stream); + ModelIO::ReadTextModel(model, stream); break; case ModelFormat::Old: - ReadOldModel(model, stream); + ModelIO::ReadOldModel(model, stream); break; } } @@ -133,785 +58,6 @@ CModel ModelInput::Read(std::istream &stream, ModelFormat format) 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: ") + boost::lexical_cast(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); - auto material = ReadBinaryMaterial(stream); - t.tex1Name = ReadBinaryString<1>(stream); - t.tex2Name = ReadBinaryString<1>(stream); - t.variableTex2 = ReadBinaryBool(stream); - - auto diffuse = Gfx::ColorToIntColor(material.diffuse); - glm::u8vec4 color = { diffuse.r, diffuse.g, diffuse.b, 255 }; - - t.p1.color = color; - t.p2.color = color; - t.p3.color = color; - - 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.material.albedoTexture = t.tex1Name; - triangle.material.detailTexture = t.tex2Name; - triangle.material.variableDetail = 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: ") + boost::lexical_cast(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"); - auto material = ParseMaterial(matText); - - auto diffuse = Gfx::ColorToIntColor(material.diffuse); - glm::u8vec4 color = { diffuse.r, diffuse.g, diffuse.b, 255 }; - - t.p1.color = color; - t.p2.color = color; - t.p3.color = color; - - 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.material.albedoTexture = t.tex1Name; - triangle.material.detailTexture = t.tex2Name; - triangle.material.variableDetail = t.variableTex2; - ConvertFromOldRenderState(triangle, t.state); - - mesh.AddTriangle(triangle); - } - - model.AddMesh("main", std::move(mesh)); -} - -void ModelInput::ReadTextModelV3(CModel &model, std::istream &stream) -{ - ModelHeaderV3 header; - - try - { - header = ReadTextHeader(stream); - } - catch (const std::exception& e) - { - throw CModelIOException(std::string("Error reading model header: ") + e.what()); - } - - for (int i = 0; i < header.totalCrashSpheres; ++i) - { - auto crashSphere = ParseCrashSphere(ReadLineString(stream, "crash_sphere")); - model.AddCrashSphere(crashSphere); - } - - if (header.hasShadowSpot) - { - auto shadowSpot = ParseShadowSpot(ReadLineString(stream, "shadow_spot")); - model.SetShadowSpot(shadowSpot); - } - - if (header.hasCameraCollisionSphere) - { - auto sphere = ParseCameraCollisionSphere(ReadLineString(stream, "camera_collision_sphere")); - model.SetCameraCollisionSphere(sphere); - } - - for (int i = 0; i < header.totalMeshes; ++i) - { - std::string meshName = ReadLineString(stream, "mesh"); - CModelMesh mesh = ReadTextMesh(stream); - model.AddMesh(meshName, std::move(mesh)); - } -} - -ModelHeaderV3 ModelInput::ReadTextHeader(std::istream &stream) -{ - ModelHeaderV3 header; - header.version = boost::lexical_cast(ReadLineString(stream, "version")); - header.totalCrashSpheres = boost::lexical_cast(ReadLineString(stream, "total_crash_spheres")); - header.hasShadowSpot = ReadLineString(stream, "has_shadow_spot") == std::string("Y"); - header.hasCameraCollisionSphere = ReadLineString(stream, "has_camera_collision_sphere") == std::string("Y"); - header.totalMeshes = boost::lexical_cast(ReadLineString(stream, "total_meshes")); - return header; -} - -CModelMesh ModelInput::ReadTextMesh(std::istream& stream) -{ - CModelMesh mesh; - - mesh.SetPosition(ParseVector(ReadLineString(stream, "position"))); - mesh.SetRotation(ParseVector(ReadLineString(stream, "rotation"))); - mesh.SetScale(ParseVector(ReadLineString(stream, "scale"))); - mesh.SetParent(ReadLineString(stream, "parent")); - - int totalTriangles = boost::lexical_cast(ReadLineString(stream, "total_triangles")); - - for (int i = 0; i < totalTriangles; ++i) - { - ModelTriangleV3 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"); - LegacyMaterial mat = ParseMaterial(matText); - - auto diffuse = Gfx::ColorToIntColor(mat.diffuse); - glm::u8vec4 color = { diffuse.r, diffuse.g, diffuse.b, 255 }; - - t.p1.color = color; - t.p2.color = color; - t.p3.color = color; - - t.material.albedoTexture = ReadLineString(stream, "tex1"); - t.material.detailTexture = ReadLineString(stream, "tex2"); - t.material.variableDetail = ReadLineString(stream, "var_tex2") == std::string("Y"); - - t.material.alphaMode = ParseTransparentMode(ReadLineString(stream, "trans_mode")); - t.material.tag = ParseSpecialMark(ReadLineString(stream, "mark")); - bool doubleSided = ReadLineString(stream, "dbl_side") == std::string("Y"); - t.material.cullFace = doubleSided ? CullFace::NONE : CullFace::BACK; - - if (t.material.alphaMode != AlphaMode::NONE) - t.material.alphaThreshold = 0.5f; - - mesh.AddTriangle(t); - } - - return mesh; -} - -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; - - for (const auto& triangle : triangles) - mesh.AddTriangle(triangle); - - 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); - - auto 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 = t.p1; - triangle.p2 = t.p2; - triangle.p3 = t.p3; - - auto diffuse = Gfx::ColorToIntColor(material.diffuse); - glm::u8vec4 color = { diffuse.r, diffuse.g, diffuse.b, 255 }; - - triangle.p1.color = color; - triangle.p2.color = color; - triangle.p3.color = color; - - 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); - - auto 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 = t.p1; - triangle.p2 = t.p2; - triangle.p3 = t.p3; - - auto diffuse = Gfx::ColorToIntColor(material.diffuse); - glm::u8vec4 color = { diffuse.r, diffuse.g, diffuse.b, 255 }; - - triangle.p1.color = color; - triangle.p2.color = color; - triangle.p3.color = color; - - 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); - - auto 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; - - auto diffuse = Gfx::ColorToIntColor(material.diffuse); - glm::u8vec4 color = { diffuse.r, diffuse.g, diffuse.b, 255 }; - - triangle.p1.color = color; - triangle.p2.color = color; - triangle.p3.color = color; - - ConvertOldTex1Name(triangle, t.texName); - - ConvertFromOldRenderState(triangle, t.state); - triangle.material.variableDetail = t.texNum2 == 1; - - if (!triangle.material.variableDetail && t.texNum2 != 0) - { - char tex2Name[20] = { 0 }; - std::sprintf(tex2Name, "dirty%.2d.png", t.texNum2); - triangle.material.detailTexture = 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.material.albedoTexture = tex1Name; - boost::replace_all(triangle.material.albedoTexture, "bmp", "png"); - boost::replace_all(triangle.material.albedoTexture, "tga", "png"); -} - -void ModelInput::ConvertFromOldRenderState(ModelTriangle& triangle, int state) -{ - if (triangle.material.albedoTexture == "plant.png" || (state & static_cast(ModelRenderState::Alpha)) != 0) - { - triangle.material.alphaMode = AlphaMode::MASK; - triangle.material.alphaThreshold = 0.5f; - } - else - triangle.material.alphaMode = AlphaMode::NONE; - - if ((state & static_cast(ModelRenderState::Part1)) != 0) - triangle.material.tag = "tracker_right"; - else if ((state & static_cast(ModelRenderState::Part2)) != 0) - triangle.material.tag = "tracker_left"; - else if ((state & static_cast(ModelRenderState::Part3)) != 0) - triangle.material.tag = "energy"; - else - triangle.material.tag = ""; - - bool doubleSided = (state & static_cast(ModelRenderState::TwoFace)) != 0; - triangle.material.cullFace = doubleSided ? CullFace::NONE : CullFace::BACK; -} - -Vertex3D ModelInput::ReadBinaryVertex(std::istream& stream) -{ - Vertex3D vertex; - - vertex.position.x = ReadBinaryFloat(stream); - vertex.position.y = ReadBinaryFloat(stream); - vertex.position.z = ReadBinaryFloat(stream); - - vertex.normal.x = ReadBinaryFloat(stream); - vertex.normal.y = ReadBinaryFloat(stream); - vertex.normal.z = ReadBinaryFloat(stream); - - vertex.uv.x = ReadBinaryFloat(stream); - vertex.uv.y = ReadBinaryFloat(stream); - - return vertex; -} - -Vertex3D ModelInput::ReadBinaryVertexTex2(std::istream& stream) -{ - Vertex3D vertex; - - vertex.position.x = ReadBinaryFloat(stream); - vertex.position.y = ReadBinaryFloat(stream); - vertex.position.z = ReadBinaryFloat(stream); - - vertex.normal.x = ReadBinaryFloat(stream); - vertex.normal.y = ReadBinaryFloat(stream); - vertex.normal.z = ReadBinaryFloat(stream); - - vertex.uv.x = ReadBinaryFloat(stream); - vertex.uv.y = ReadBinaryFloat(stream); - - vertex.uv2.x = ReadBinaryFloat(stream); - vertex.uv2.y = ReadBinaryFloat(stream); - - return vertex; -} - -LegacyMaterial ModelInput::ReadBinaryMaterial(std::istream& stream) -{ - LegacyMaterial 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); - boost::trim_right(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; -} - -void ModelInput::ReadValuePrefix(std::istream& stream, const std::string& expectedPrefix) -{ - std::string prefix; - stream >> prefix; - if (prefix != expectedPrefix) - throw CModelIOException(std::string("Unexpected prefix: '") + prefix + "', expected was: '" + expectedPrefix + "'"); -} - -Vertex3D ModelInput::ParseVertexTex2(const std::string& text) -{ - Vertex3D vertex; - - std::stringstream stream; - stream.str(text); - - ReadValuePrefix(stream, "c"); - stream >> vertex.position.x >> vertex.position.y >> vertex.position.z; - - ReadValuePrefix(stream, "n"); - stream >> vertex.normal.x >> vertex.normal.y >> vertex.normal.z; - - ReadValuePrefix(stream, "t1"); - stream >> vertex.uv.x >> vertex.uv.y; - - ReadValuePrefix(stream, "t2"); - stream >> vertex.uv2.x >> vertex.uv2.y; - - return vertex; -} - -LegacyMaterial ModelInput::ParseMaterial(const std::string& text) -{ - LegacyMaterial material; - - std::stringstream stream; - stream.str(text); - - ReadValuePrefix(stream, "dif"); - stream >> material.diffuse.r - >> material.diffuse.g - >> material.diffuse.b - >> material.diffuse.a; - - ReadValuePrefix(stream, "amb"); - stream >> material.ambient.r - >> material.ambient.g - >> material.ambient.b - >> material.ambient.a; - - ReadValuePrefix(stream, "spc"); - stream >> material.specular.r - >> material.specular.g - >> material.specular.b - >> material.specular.a; - - return material; -} - -glm::vec3 ModelInput::ParseVector(const std::string& text) -{ - glm::vec3 vector = { 0, 0, 0 }; - - std::stringstream stream; - stream.str(text); - - stream >> vector.x >> vector.y >> vector.z; - - return vector; -} - -ModelCrashSphere ModelInput::ParseCrashSphere(const std::string& text) -{ - ModelCrashSphere crashSphere; - - std::stringstream stream; - stream.str(text); - - ReadValuePrefix(stream, "pos"); - stream >> crashSphere.position.x - >> crashSphere.position.y - >> crashSphere.position.z; - - ReadValuePrefix(stream, "rad"); - stream >> crashSphere.radius; - - ReadValuePrefix(stream, "sound"); - stream >> crashSphere.sound; - - ReadValuePrefix(stream, "hard"); - stream >> crashSphere.hardness; - - return crashSphere; -} - -ModelShadowSpot ModelInput::ParseShadowSpot(const std::string& text) -{ - ModelShadowSpot shadowSpot; - - std::stringstream stream; - stream.str(text); - - ReadValuePrefix(stream, "rad"); - stream >> shadowSpot.radius; - - ReadValuePrefix(stream, "int"); - stream >> shadowSpot.intensity; - - return shadowSpot; -} - -Math::Sphere ModelInput::ParseCameraCollisionSphere(const std::string& text) -{ - Math::Sphere sphere; - - std::stringstream stream; - stream.str(text); - - ReadValuePrefix(stream, "pos"); - stream >> sphere.pos.x - >> sphere.pos.y - >> sphere.pos.z; - - ReadValuePrefix(stream, "rad"); - stream >> sphere.radius; - - return sphere; -} - -AlphaMode ModelInput::ParseTransparentMode(const std::string& text) -{ - if (text == "none") - return AlphaMode::NONE; - else if (text == "alpha") - return AlphaMode::MASK; - else - return AlphaMode::NONE; -} - -std::string ModelInput::ParseSpecialMark(const std::string& text) -{ - if (text == "none") - return ""; - else if (text == "part1") - return "tracker_right"; - else if (text == "part2") - return "tracker_left"; - else if (text == "part3") - return "energy"; - else - throw CModelIOException(std::string("Unexpected special mark: '") + text + "'"); -} } // namespace Gfx diff --git a/src/graphics/model/model_io_structs.h b/src/graphics/model/model_io_structs.h index 7b3c279a..613617f7 100644 --- a/src/graphics/model/model_io_structs.h +++ b/src/graphics/model/model_io_structs.h @@ -57,6 +57,27 @@ enum class ModelRenderState Alpha = 8192 //!< old ENG_RSTATE_ALPHA }; +//! Legacy material structure +struct LegacyMaterial +{ + //! Diffuse color + Color diffuse; + //! Ambient color + Color ambient; + //! Specular color + Color specular; + + bool operator==(const LegacyMaterial& mat) const + { + return diffuse == mat.diffuse && ambient == mat.ambient && specular == mat.specular; + } + + bool operator!=(const LegacyMaterial& mat) const + { + return !operator==(mat); + } +}; + /******************************************************* New model formats *******************************************************/ diff --git a/src/graphics/model/model_mod.cpp b/src/graphics/model/model_mod.cpp new file mode 100644 index 00000000..bd082b35 --- /dev/null +++ b/src/graphics/model/model_mod.cpp @@ -0,0 +1,371 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2022, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.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/model/model_mod.h" + +#include "common/ioutils.h" +#include "common/resources/inputstream.h" + +#include "graphics/model/model_io_exception.h" +#include "graphics/model/model_io_structs.h" + +#include +#include + +#include + +using namespace IOUtils; + +namespace Gfx::ModelIO +{ + +std::vector ReadOldModelV1(std::istream& stream, int totalTriangles); +std::vector ReadOldModelV2(std::istream& stream, int totalTriangles); +std::vector ReadOldModelV3(std::istream& stream, int totalTriangles); + +Vertex3D ReadBinaryVertex(std::istream& stream); +Vertex3D ReadBinaryVertexTex2(std::istream& stream); +LegacyMaterial ReadBinaryMaterial(std::istream& stream); + +void ConvertOldTex1Name(ModelTriangle& triangle, const char* tex1Name); +void ConvertFromOldRenderState(ModelTriangle& triangle, int state); +ModelLODLevel MinMaxToLodLevel(float min, float max); + +void 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; + + for (const auto& triangle : triangles) + mesh.AddTriangle(triangle); + + model.AddMesh("main", std::move(mesh)); +} + +std::vector 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); + + auto 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 = t.p1; + triangle.p2 = t.p2; + triangle.p3 = t.p3; + + auto diffuse = Gfx::ColorToIntColor(material.diffuse); + glm::u8vec4 color = { diffuse.r, diffuse.g, diffuse.b, 255 }; + + triangle.p1.color = color; + triangle.p2.color = color; + triangle.p3.color = color; + + ConvertOldTex1Name(triangle, t.texName); + + triangles.push_back(triangle); + } + + return triangles; +} + +std::vector 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); + + auto 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 = t.p1; + triangle.p2 = t.p2; + triangle.p3 = t.p3; + + auto diffuse = Gfx::ColorToIntColor(material.diffuse); + glm::u8vec4 color = { diffuse.r, diffuse.g, diffuse.b, 255 }; + + triangle.p1.color = color; + triangle.p2.color = color; + triangle.p3.color = color; + + ConvertOldTex1Name(triangle, t.texName); + + ConvertFromOldRenderState(triangle, t.state); + + triangles.push_back(triangle); + } + + return triangles; +} + +std::vector 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); + + auto 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; + + auto diffuse = Gfx::ColorToIntColor(material.diffuse); + glm::u8vec4 color = { diffuse.r, diffuse.g, diffuse.b, 255 }; + + triangle.p1.color = color; + triangle.p2.color = color; + triangle.p3.color = color; + + ConvertOldTex1Name(triangle, t.texName); + + ConvertFromOldRenderState(triangle, t.state); + triangle.material.variableDetail = t.texNum2 == 1; + + if (!triangle.material.variableDetail && t.texNum2 != 0) + { + char tex2Name[20] = { 0 }; + std::sprintf(tex2Name, "dirty%.2d.png", t.texNum2); + triangle.material.detailTexture = tex2Name; + } + + triangles.push_back(triangle); + } + + return triangles; +} + +ModelLODLevel 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 ConvertOldTex1Name(ModelTriangle& triangle, const char* tex1Name) +{ + triangle.material.albedoTexture = tex1Name; + boost::replace_all(triangle.material.albedoTexture, "bmp", "png"); + boost::replace_all(triangle.material.albedoTexture, "tga", "png"); +} + +void ConvertFromOldRenderState(ModelTriangle& triangle, int state) +{ + if (triangle.material.albedoTexture == "plant.png" || (state & static_cast(ModelRenderState::Alpha)) != 0) + { + triangle.material.alphaMode = AlphaMode::MASK; + triangle.material.alphaThreshold = 0.5f; + } + else + triangle.material.alphaMode = AlphaMode::NONE; + + if ((state & static_cast(ModelRenderState::Part1)) != 0) + triangle.material.tag = "tracker_right"; + else if ((state & static_cast(ModelRenderState::Part2)) != 0) + triangle.material.tag = "tracker_left"; + else if ((state & static_cast(ModelRenderState::Part3)) != 0) + triangle.material.tag = "energy"; + else + triangle.material.tag = ""; + + bool doubleSided = (state & static_cast(ModelRenderState::TwoFace)) != 0; + triangle.material.cullFace = doubleSided ? CullFace::NONE : CullFace::BACK; +} + +Vertex3D ReadBinaryVertex(std::istream& stream) +{ + Vertex3D vertex; + + vertex.position.x = ReadBinaryFloat(stream); + vertex.position.y = ReadBinaryFloat(stream); + vertex.position.z = ReadBinaryFloat(stream); + + vertex.normal.x = ReadBinaryFloat(stream); + vertex.normal.y = ReadBinaryFloat(stream); + vertex.normal.z = ReadBinaryFloat(stream); + + vertex.uv.x = ReadBinaryFloat(stream); + vertex.uv.y = ReadBinaryFloat(stream); + + return vertex; +} + +Vertex3D ReadBinaryVertexTex2(std::istream& stream) +{ + Vertex3D vertex; + + vertex.position.x = ReadBinaryFloat(stream); + vertex.position.y = ReadBinaryFloat(stream); + vertex.position.z = ReadBinaryFloat(stream); + + vertex.normal.x = ReadBinaryFloat(stream); + vertex.normal.y = ReadBinaryFloat(stream); + vertex.normal.z = ReadBinaryFloat(stream); + + vertex.uv.x = ReadBinaryFloat(stream); + vertex.uv.y = ReadBinaryFloat(stream); + + vertex.uv2.x = ReadBinaryFloat(stream); + vertex.uv2.y = ReadBinaryFloat(stream); + + return vertex; +} + +LegacyMaterial ReadBinaryMaterial(std::istream& stream) +{ + LegacyMaterial 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; +} + +} diff --git a/src/graphics/model/model_mod.h b/src/graphics/model/model_mod.h new file mode 100644 index 00000000..61232e0d --- /dev/null +++ b/src/graphics/model/model_mod.h @@ -0,0 +1,36 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2022, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.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 + */ + +#pragma once + +#include "graphics/model/model.h" +#include "graphics/model/model_format.h" + +#include + +/** + * \namespace ModelInput + * \brief Namespace with functions to read model files + */ +namespace Gfx::ModelIO +{ + +void ReadOldModel(CModel& model, std::istream& stream); + +} diff --git a/src/graphics/model/model_txt.cpp b/src/graphics/model/model_txt.cpp new file mode 100644 index 00000000..fe188401 --- /dev/null +++ b/src/graphics/model/model_txt.cpp @@ -0,0 +1,425 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2022, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.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/model/model_mod.h" + +#include "common/ioutils.h" +#include "common/resources/inputstream.h" + +#include "graphics/model/model_io_exception.h" +#include "graphics/model/model_io_structs.h" + +#include +#include + +using namespace IOUtils; + +namespace Gfx::ModelIO +{ + +void ReadTextModelV1AndV2(CModel& model, std::istream& stream); +void ReadTextModelV3(CModel& model, std::istream& stream); +ModelHeaderV3 ReadTextHeader(std::istream& stream); +CModelMesh ReadTextMesh(std::istream& stream); + +std::string ReadLineString(std::istream& stream, const std::string& expectedPrefix); +void ReadValuePrefix(std::istream& stream, const std::string& expectedPrefix); +Vertex3D ParseVertexTex2(const std::string& text); +LegacyMaterial ParseMaterial(const std::string& text); +glm::vec3 ParseVector(const std::string& text); +ModelCrashSphere ParseCrashSphere(const std::string& text); +ModelShadowSpot ParseShadowSpot(const std::string& text); +Math::Sphere ParseCameraCollisionSphere(const std::string& text); +AlphaMode ParseTransparentMode(const std::string& text); +std::string ParseSpecialMark(const std::string& text); + +void ConvertFromOldRenderState(ModelTriangle& triangle, int state); + +void 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: ") + boost::lexical_cast(version)); +} + +void 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"); + auto material = ParseMaterial(matText); + + auto diffuse = Gfx::ColorToIntColor(material.diffuse); + glm::u8vec4 color = { diffuse.r, diffuse.g, diffuse.b, 255 }; + + t.p1.color = color; + t.p2.color = color; + t.p3.color = color; + + 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.material.albedoTexture = t.tex1Name; + triangle.material.detailTexture = t.tex2Name; + triangle.material.variableDetail = t.variableTex2; + ConvertFromOldRenderState(triangle, t.state); + + mesh.AddTriangle(triangle); + } + + model.AddMesh("main", std::move(mesh)); +} + +void ReadTextModelV3(CModel& model, std::istream& stream) +{ + ModelHeaderV3 header; + + try + { + header = ReadTextHeader(stream); + } + catch (const std::exception& e) + { + throw CModelIOException(std::string("Error reading model header: ") + e.what()); + } + + for (int i = 0; i < header.totalCrashSpheres; ++i) + { + auto crashSphere = ParseCrashSphere(ReadLineString(stream, "crash_sphere")); + model.AddCrashSphere(crashSphere); + } + + if (header.hasShadowSpot) + { + auto shadowSpot = ParseShadowSpot(ReadLineString(stream, "shadow_spot")); + model.SetShadowSpot(shadowSpot); + } + + if (header.hasCameraCollisionSphere) + { + auto sphere = ParseCameraCollisionSphere(ReadLineString(stream, "camera_collision_sphere")); + model.SetCameraCollisionSphere(sphere); + } + + for (int i = 0; i < header.totalMeshes; ++i) + { + std::string meshName = ReadLineString(stream, "mesh"); + CModelMesh mesh = ReadTextMesh(stream); + model.AddMesh(meshName, std::move(mesh)); + } +} + +ModelHeaderV3 ReadTextHeader(std::istream& stream) +{ + ModelHeaderV3 header; + header.version = boost::lexical_cast(ReadLineString(stream, "version")); + header.totalCrashSpheres = boost::lexical_cast(ReadLineString(stream, "total_crash_spheres")); + header.hasShadowSpot = ReadLineString(stream, "has_shadow_spot") == std::string("Y"); + header.hasCameraCollisionSphere = ReadLineString(stream, "has_camera_collision_sphere") == std::string("Y"); + header.totalMeshes = boost::lexical_cast(ReadLineString(stream, "total_meshes")); + return header; +} + +CModelMesh ReadTextMesh(std::istream& stream) +{ + CModelMesh mesh; + + mesh.SetPosition(ParseVector(ReadLineString(stream, "position"))); + mesh.SetRotation(ParseVector(ReadLineString(stream, "rotation"))); + mesh.SetScale(ParseVector(ReadLineString(stream, "scale"))); + mesh.SetParent(ReadLineString(stream, "parent")); + + int totalTriangles = boost::lexical_cast(ReadLineString(stream, "total_triangles")); + + for (int i = 0; i < totalTriangles; ++i) + { + ModelTriangleV3 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"); + LegacyMaterial mat = ParseMaterial(matText); + + auto diffuse = Gfx::ColorToIntColor(mat.diffuse); + glm::u8vec4 color = { diffuse.r, diffuse.g, diffuse.b, 255 }; + + t.p1.color = color; + t.p2.color = color; + t.p3.color = color; + + t.material.albedoTexture = ReadLineString(stream, "tex1"); + t.material.detailTexture = ReadLineString(stream, "tex2"); + t.material.variableDetail = ReadLineString(stream, "var_tex2") == std::string("Y"); + + t.material.alphaMode = ParseTransparentMode(ReadLineString(stream, "trans_mode")); + t.material.tag = ParseSpecialMark(ReadLineString(stream, "mark")); + bool doubleSided = ReadLineString(stream, "dbl_side") == std::string("Y"); + t.material.cullFace = doubleSided ? CullFace::NONE : CullFace::BACK; + + if (t.material.alphaMode != AlphaMode::NONE) + t.material.alphaThreshold = 0.5f; + + mesh.AddTriangle(t); + } + + return mesh; +} + + + + +std::string ReadLineString(std::istream& stream, const std::string& expectedPrefix) +{ + std::string line; + while (true) + { + if (stream.eof()) + throw CModelIOException("Unexpected EOF"); + + std::getline(stream, line); + boost::trim_right(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; +} + +void ReadValuePrefix(std::istream& stream, const std::string& expectedPrefix) +{ + std::string prefix; + stream >> prefix; + if (prefix != expectedPrefix) + throw CModelIOException(std::string("Unexpected prefix: '") + prefix + "', expected was: '" + expectedPrefix + "'"); +} + +Vertex3D ParseVertexTex2(const std::string& text) +{ + Vertex3D vertex; + + std::stringstream stream; + stream.str(text); + + ReadValuePrefix(stream, "c"); + stream >> vertex.position.x >> vertex.position.y >> vertex.position.z; + + ReadValuePrefix(stream, "n"); + stream >> vertex.normal.x >> vertex.normal.y >> vertex.normal.z; + + ReadValuePrefix(stream, "t1"); + stream >> vertex.uv.x >> vertex.uv.y; + + ReadValuePrefix(stream, "t2"); + stream >> vertex.uv2.x >> vertex.uv2.y; + + return vertex; +} + +LegacyMaterial ParseMaterial(const std::string& text) +{ + LegacyMaterial material; + + std::stringstream stream; + stream.str(text); + + ReadValuePrefix(stream, "dif"); + stream >> material.diffuse.r + >> material.diffuse.g + >> material.diffuse.b + >> material.diffuse.a; + + ReadValuePrefix(stream, "amb"); + stream >> material.ambient.r + >> material.ambient.g + >> material.ambient.b + >> material.ambient.a; + + ReadValuePrefix(stream, "spc"); + stream >> material.specular.r + >> material.specular.g + >> material.specular.b + >> material.specular.a; + + return material; +} + +glm::vec3 ParseVector(const std::string& text) +{ + glm::vec3 vector = { 0, 0, 0 }; + + std::stringstream stream; + stream.str(text); + + stream >> vector.x >> vector.y >> vector.z; + + return vector; +} + +ModelCrashSphere ParseCrashSphere(const std::string& text) +{ + ModelCrashSphere crashSphere; + + std::stringstream stream; + stream.str(text); + + ReadValuePrefix(stream, "pos"); + stream >> crashSphere.position.x + >> crashSphere.position.y + >> crashSphere.position.z; + + ReadValuePrefix(stream, "rad"); + stream >> crashSphere.radius; + + ReadValuePrefix(stream, "sound"); + stream >> crashSphere.sound; + + ReadValuePrefix(stream, "hard"); + stream >> crashSphere.hardness; + + return crashSphere; +} + +ModelShadowSpot ParseShadowSpot(const std::string& text) +{ + ModelShadowSpot shadowSpot; + + std::stringstream stream; + stream.str(text); + + ReadValuePrefix(stream, "rad"); + stream >> shadowSpot.radius; + + ReadValuePrefix(stream, "int"); + stream >> shadowSpot.intensity; + + return shadowSpot; +} + +Math::Sphere ParseCameraCollisionSphere(const std::string& text) +{ + Math::Sphere sphere; + + std::stringstream stream; + stream.str(text); + + ReadValuePrefix(stream, "pos"); + stream >> sphere.pos.x + >> sphere.pos.y + >> sphere.pos.z; + + ReadValuePrefix(stream, "rad"); + stream >> sphere.radius; + + return sphere; +} + +AlphaMode ParseTransparentMode(const std::string& text) +{ + if (text == "none") + return AlphaMode::NONE; + else if (text == "alpha") + return AlphaMode::MASK; + else + return AlphaMode::NONE; +} + +std::string ParseSpecialMark(const std::string& text) +{ + if (text == "none") + return ""; + else if (text == "part1") + return "tracker_right"; + else if (text == "part2") + return "tracker_left"; + else if (text == "part3") + return "energy"; + else + throw CModelIOException(std::string("Unexpected special mark: '") + text + "'"); +} + +} diff --git a/src/graphics/model/model_txt.h b/src/graphics/model/model_txt.h new file mode 100644 index 00000000..80b87229 --- /dev/null +++ b/src/graphics/model/model_txt.h @@ -0,0 +1,36 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2022, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.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 + */ + +#pragma once + +#include "graphics/model/model.h" +#include "graphics/model/model_format.h" + +#include + +/** + * \namespace ModelInput + * \brief Namespace with functions to read model files + */ +namespace Gfx::ModelIO +{ + +void ReadTextModel(CModel& model, std::istream& stream); + +}