summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt5
-rw-r--r--src/app/app.cpp29
-rw-r--r--src/common/ioutils.h80
-rw-r--r--src/common/singleton.h10
-rw-r--r--src/graphics/engine/modelfile.cpp1037
-rw-r--r--src/graphics/engine/modelfile.h62
-rw-r--r--src/graphics/engine/terrain.cpp2
-rw-r--r--src/graphics/engine/terrain.h2
-rw-r--r--src/tools/CMakeLists.txt13
-rw-r--r--src/tools/README.txt4
-rw-r--r--src/tools/convert_model.cpp285
11 files changed, 1179 insertions, 350 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index b998d19..ff271fd 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,5 +1,8 @@
# CBot shared library is built separately
-add_subdirectory(CBot)
+add_subdirectory(CBot)
+
+# Tools directory is built separately
+add_subdirectory(tools)
# Configure options
diff --git a/src/app/app.cpp b/src/app/app.cpp
index 182e0fd..00cd13d 100644
--- a/src/app/app.cpp
+++ b/src/app/app.cpp
@@ -113,6 +113,7 @@ CApplication::~CApplication()
bool CApplication::ParseArguments(int argc, char *argv[])
{
bool waitDataDir = false;
+ bool waitLogLevel = false;
for (int i = 1; i < argc; ++i)
{
@@ -125,10 +126,34 @@ bool CApplication::ParseArguments(int argc, char *argv[])
continue;
}
+ if (waitLogLevel)
+ {
+ waitLogLevel = false;
+ if (arg == "trace")
+ GetLogger()->SetLogLevel(LOG_TRACE);
+ else if (arg == "debug")
+ GetLogger()->SetLogLevel(LOG_DEBUG);
+ else if (arg == "info")
+ GetLogger()->SetLogLevel(LOG_INFO);
+ else if (arg == "warn")
+ GetLogger()->SetLogLevel(LOG_WARN);
+ else if (arg == "error")
+ GetLogger()->SetLogLevel(LOG_ERROR);
+ else if (arg == "none")
+ GetLogger()->SetLogLevel(LOG_NONE);
+ else
+ return false;
+ continue;
+ }
+
if (arg == "-debug")
{
SetDebugMode(true);
}
+ else if (arg == "-loglevel")
+ {
+ waitLogLevel = true;
+ }
else if (arg == "-datadir")
{
waitDataDir = true;
@@ -140,8 +165,8 @@ bool CApplication::ParseArguments(int argc, char *argv[])
}
}
- // Data dir not given?
- if (waitDataDir)
+ // Args not given?
+ if (waitDataDir || waitLogLevel)
return false;
return true;
diff --git a/src/common/ioutils.h b/src/common/ioutils.h
index a7bd044..2a542c6 100644
--- a/src/common/ioutils.h
+++ b/src/common/ioutils.h
@@ -27,9 +27,10 @@ namespace IOUtils {
//! Writes a binary number to output stream
/**
- \c T is a numeric type (int, unsigned int, etc.)
- \c N is number of bytes
- Write order is little-endian */
+ * \c T is a numeric type (int, unsigned int, etc.)
+ * \c N is number of bytes
+ * Write order is little-endian
+ */
template<int N, typename T>
void WriteBinary(T value, std::ostream &ostr)
{
@@ -42,9 +43,10 @@ void WriteBinary(T value, std::ostream &ostr)
//! Reads a binary number from input stream
/**
- \c T is a numeric type (int, unsigned int, etc.)
- \c N is number of bytes
- Read order is little-endian */
+ * \c T is a numeric type (int, unsigned int, etc.)
+ * \c N is number of bytes
+ * Read order is little-endian
+ */
template<int N, typename T>
T ReadBinary(std::istream &istr)
{
@@ -58,10 +60,31 @@ T ReadBinary(std::istream &istr)
return value;
}
+//! Writes a binary 1-byte boolean
+/**
+ * false is 0; true is 1.
+ */
+void WriteBinaryBool(float value, std::ostream &ostr)
+{
+ unsigned char v = value ? 1 : 0;
+ IOUtils::WriteBinary<1, unsigned char>(v, ostr);
+}
+
+//! Reads a binary 1-byte boolean
+/**
+ * 0 is false; other values are true.
+ */
+bool ReadBinaryBool(std::istream &istr)
+{
+ int v = IOUtils::ReadBinary<1, unsigned char>(istr);
+ return v != 0;
+}
+
//! Writes a binary 32-bit float to output stream
/**
- Write order is little-endian
- NOTE: code is probably not portable as there are platforms with other float representations. */
+ * 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)
{
union { float fValue; unsigned int iValue; } u;
@@ -72,8 +95,9 @@ void WriteBinaryFloat(float value, std::ostream &ostr)
//! Reads a binary 32-bit float from input stream
/**
- Read order is little-endian
- NOTE: code is probably not portable as there are platforms with other float representations. */
+ * Read order is little-endian
+ * NOTE: code is probably not portable as there are platforms with other float representations.
+ */
float ReadBinaryFloat(std::istream &istr)
{
union { float fValue; unsigned int iValue; } u;
@@ -82,4 +106,40 @@ float ReadBinaryFloat(std::istream &istr)
return u.fValue;
}
+//! Writes a variable binary string to output stream
+/**
+ * The string is written by first writing string length
+ * in \c N byte binary number and then the string bytes.
+ */
+template<int N>
+void WriteBinaryString(const std::string &value, std::ostream &ostr)
+{
+ int length = value.size();
+ WriteBinary<N, int>(length, ostr);
+
+ for (int i = 0; i < length; ++i)
+ ostr.put(value[i]);
+}
+
+//! Reads a variable binary string from output stream
+/**
+ * The string is read by first reading string length
+ * in \c N byte binary number and then the string bytes.
+ */
+template<int N>
+std::string ReadBinaryString(std::istream &istr)
+{
+ int length = ReadBinary<N, int>(istr);
+
+ std::string str;
+ char c = 0;
+ for (int i = 0; i < length; ++i)
+ {
+ istr.read(&c, 1);
+ str += c;
+ }
+
+ return str;
+}
+
}; // namespace IOUtils
diff --git a/src/common/singleton.h b/src/common/singleton.h
index 4df7878..f631ed4 100644
--- a/src/common/singleton.h
+++ b/src/common/singleton.h
@@ -29,26 +29,26 @@ template<typename T> class CSingleton
public:
static T& GetInstance() {
- assert(mInstance != NULL);
+ assert(mInstance != nullptr);
return *mInstance;
}
static T* GetInstancePointer() {
- assert(mInstance != NULL);
+ assert(mInstance != nullptr);
return mInstance;
}
static bool IsCreated() {
- return mInstance != NULL;
+ return mInstance != nullptr;
}
CSingleton() {
- assert(mInstance == NULL);
+ assert(mInstance == nullptr);
mInstance = static_cast<T *>(this);
}
virtual ~CSingleton() {
- mInstance = NULL;
+ mInstance = nullptr;
}
private:
diff --git a/src/graphics/engine/modelfile.cpp b/src/graphics/engine/modelfile.cpp
index 9d7a389..20a0932 100644
--- a/src/graphics/engine/modelfile.cpp
+++ b/src/graphics/engine/modelfile.cpp
@@ -23,104 +23,28 @@
#include "common/ioutils.h"
#include "common/logger.h"
#include "common/stringutils.h"
+#include "graphics/engine/engine.h"
#include "math/geometry.h"
#include <string.h>
#include <fstream>
+#include <sstream>
-//! How big the triangle vector is by default
-const int TRIANGLE_PREALLOCATE_COUNT = 2000;
-
-/**
- \struct ModelHeader
- \brief Header info for model file
+/*
+ * NOTE: #ifndef checking for MODELFILE_NO_ENGINE
+ * is provided in this module to conditionally
+ * disable dependence on CEngine.
*/
-struct ModelHeader
-{
- //! Revision number
- int revision;
- //! Version number
- int version;
- //! Total number of vertices
- int totalVertices;
- //! Reserved area
- int reserved[10];
-
- ModelHeader()
- {
- memset(this, 0, sizeof(*this));
- }
-};
-
-
-struct OldModelTriangle1
-{
- char used;
- char selected;
- Gfx::Vertex p1;
- Gfx::Vertex p2;
- Gfx::Vertex p3;
- Gfx::Material material;
- char texName[20];
- float min;
- float max;
-
- OldModelTriangle1()
- {
- memset(this, 0, sizeof(*this));
- }
-};
-
-struct OldModelTriangle2
-{
- char used;
- char selected;
- Gfx::Vertex p1;
- Gfx::Vertex p2;
- Gfx::Vertex p3;
- Gfx::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 NewModelTriangle
-{
- char used;
- char selected;
- Gfx::VertexTex2 p1;
- Gfx::VertexTex2 p2;
- Gfx::VertexTex2 p3;
- Gfx::Material material;
- char texName[20];
- float min;
- float max;
- long state;
- short texNum2;
- short reserved2;
- short reserved3;
- short reserved4;
+//! How big the triangle vector is by default
+const int TRIANGLE_PREALLOCATE_COUNT = 2000;
- NewModelTriangle()
- {
- memset(this, 0, sizeof(*this));
- }
-};
-Gfx::Vertex ReadBinaryVertex(std::istream &stream)
+Gfx::Vertex ReadBinaryVertex(std::istream& stream)
{
Gfx::Vertex result;
@@ -136,7 +60,7 @@ Gfx::Vertex ReadBinaryVertex(std::istream &stream)
return result;
}
-void WriteBinaryVertex(Gfx::Vertex vertex, std::ostream &stream)
+void WriteBinaryVertex(Gfx::Vertex vertex, std::ostream& stream)
{
IOUtils::WriteBinaryFloat(vertex.coord.x, stream);
IOUtils::WriteBinaryFloat(vertex.coord.y, stream);
@@ -148,7 +72,7 @@ void WriteBinaryVertex(Gfx::Vertex vertex, std::ostream &stream)
IOUtils::WriteBinaryFloat(vertex.texCoord.y, stream);
}
-Gfx::VertexTex2 ReadBinaryVertexTex2(std::istream &stream)
+Gfx::VertexTex2 ReadBinaryVertexTex2(std::istream& stream)
{
Gfx::VertexTex2 result;
@@ -166,7 +90,7 @@ Gfx::VertexTex2 ReadBinaryVertexTex2(std::istream &stream)
return result;
}
-void WriteBinaryVertexTex2(Gfx::VertexTex2 vertex, std::ostream &stream)
+void WriteBinaryVertexTex2(Gfx::VertexTex2 vertex, std::ostream& stream)
{
IOUtils::WriteBinaryFloat(vertex.coord.x, stream);
IOUtils::WriteBinaryFloat(vertex.coord.y, stream);
@@ -180,7 +104,63 @@ void WriteBinaryVertexTex2(Gfx::VertexTex2 vertex, std::ostream &stream)
IOUtils::WriteBinaryFloat(vertex.texCoord2.y, stream);
}
-Gfx::Material ReadBinaryMaterial(std::istream &stream)
+Gfx::VertexTex2 ReadTextVertexTex2(const std::string& text)
+{
+ std::stringstream stream;
+ stream.str(text);
+
+ Gfx::VertexTex2 result;
+ std::string what;
+
+ stream >> what;
+ if (what != "c")
+ {
+ GetLogger()->Error("c\n");
+ return Gfx::VertexTex2();
+ }
+
+ stream >> result.coord.x >> result.coord.y >> result.coord.z;
+
+ stream >> what;
+ if (what != "n")
+ {
+ GetLogger()->Error("n\n");
+ return Gfx::VertexTex2();
+ }
+
+ stream >> result.normal.x >> result.normal.y >> result.normal.z;
+
+ stream >> what;
+ if (what != "t1")
+ {
+ GetLogger()->Error("t1\n");
+ return Gfx::VertexTex2();
+ }
+
+ stream >> result.texCoord.x >> result.texCoord.y;
+
+ stream >> what;
+ if (what != "t2")
+ {
+ GetLogger()->Error("t2\n");
+ return Gfx::VertexTex2();
+ }
+
+ stream >> result.texCoord2.x >> result.texCoord2.y;
+
+ return result;
+}
+
+void WriteTextVertexTex2(const Gfx::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.texCoord.x << " " << vertex.texCoord.y;
+ stream << std::endl;
+}
+
+Gfx::Material ReadBinaryMaterial(std::istream& stream)
{
Gfx::Material result;
@@ -209,7 +189,7 @@ Gfx::Material ReadBinaryMaterial(std::istream &stream)
return result;
}
-void WriteBinaryMaterial(Gfx::Material material, std::ostream &stream)
+void WriteBinaryMaterial(const Gfx::Material& material, std::ostream& stream)
{
IOUtils::WriteBinaryFloat(material.diffuse.r, stream);
IOUtils::WriteBinaryFloat(material.diffuse.g, stream);
@@ -234,11 +214,87 @@ void WriteBinaryMaterial(Gfx::Material material, std::ostream &stream)
/* power */ IOUtils::WriteBinaryFloat(0.0f, stream);
}
-Gfx::ModelTriangle::ModelTriangle()
+Gfx::Material ReadTextMaterial(const std::string& text)
+{
+ std::stringstream stream;
+ stream.str(text);
+
+ Gfx::Material result;
+ std::string what;
+
+ stream >> what;
+ if (what != "dif")
+ return Gfx::Material();
+
+ stream >> result.diffuse.r >> result.diffuse.g >> result.diffuse.b >> result.diffuse.a;
+
+ stream >> what;
+ if (what != "amb")
+ return Gfx::Material();
+
+ stream >> result.ambient.r >> result.ambient.g >> result.ambient.b >> result.ambient.a;
+
+ stream >> what;
+ if (what != "spc")
+ return Gfx::Material();
+
+ stream >> result.specular.r >> result.specular.g >> result.specular.b >> result.specular.a;
+
+ return result;
+}
+
+void WriteTextMaterial(const Gfx::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 << std::endl;
+}
+
+template<typename T>
+bool ReadLineValue(std::istream& stream, const std::string& prefix, T& value)
+{
+ std::string line;
+ while (! stream.eof() )
+ {
+ 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)
{
- min = 0.0f;
- max = 0.0f;
- state = 0L;
+ std::string line;
+ while (! stream.eof() )
+ {
+ 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;
+
+ getline(s, value);
+
+ return true;
}
@@ -246,7 +302,9 @@ Gfx::CModelFile::CModelFile(CInstanceManager* iMan)
{
m_iMan = iMan;
+#ifndef MODELFILE_NO_ENGINE
m_engine = static_cast<CEngine*>(m_iMan->SearchInstance(CLASS_ENGINE));
+#endif
m_triangles.reserve(TRIANGLE_PREALLOCATE_COUNT);
}
@@ -255,54 +313,154 @@ Gfx::CModelFile::~CModelFile()
{
}
-std::string Gfx::CModelFile::GetError()
+
+/*******************************************************
+ Deprecated formats
+ *******************************************************/
+
+/**
+ * \struct OldModelHeader
+ * \brief Colobot binary model header info
+ *
+ * @deprecated
+ */
+struct OldModelHeader
{
- return m_error;
-}
+ //! 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));
+ }
+};
-bool Gfx::CModelFile::ReadModel(const std::string &filename, bool edit, bool meta)
+/**
+ * \struct OldModelTriangle1
+ * \brief Colobot binary model file version 1
+ *
+ * @deprecated
+ */
+struct OldModelTriangle1
+{
+ char used;
+ char selected;
+ Gfx::Vertex p1;
+ Gfx::Vertex p2;
+ Gfx::Vertex p3;
+ Gfx::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;
+ Gfx::Vertex p1;
+ Gfx::Vertex p2;
+ Gfx::Vertex p3;
+ Gfx::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;
+ Gfx::VertexTex2 p1;
+ Gfx::VertexTex2 p2;
+ Gfx::VertexTex2 p3;
+ Gfx::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 Gfx::CModelFile::ReadModel(const std::string& fileName)
{
m_triangles.clear();
- m_error = "";
std::ifstream stream;
- stream.open(filename.c_str(), std::ios_base::in | std::ios_base::binary);
+ stream.open(fileName.c_str(), std::ios_base::in | std::ios_base::binary);
if (! stream.good())
{
- m_error = std::string("Could not open file '") + filename + std::string("'");
+ GetLogger()->Error("Could not open file '%s'\n", fileName.c_str());
return false;
}
- return ReadModel(stream, edit, meta);
+ return ReadModel(stream);
}
-bool Gfx::CModelFile::ReadModel(std::istream &stream, bool edit, bool meta)
+bool Gfx::CModelFile::ReadModel(std::istream& stream)
{
m_triangles.clear();
- m_error = "";
- // FIXME: for now, reading models only from files, not metafile
-
- ModelHeader header;
+ OldModelHeader header;
header.revision = IOUtils::ReadBinary<4, int>(stream);
header.version = IOUtils::ReadBinary<4, int>(stream);
- header.totalVertices = 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())
{
- m_error = "Error reading model file header";
+ 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.totalVertices; ++i)
+ for (int i = 0; i < header.totalTriangles; ++i)
{
OldModelTriangle1 t;
t.used = IOUtils::ReadBinary<1, char>(stream);
@@ -319,7 +477,7 @@ bool Gfx::CModelFile::ReadModel(std::istream &stream, bool edit, bool meta)
if (! stream.good())
{
- m_error = "Error reading model data";
+ GetLogger()->Error("Error reading model data\n");
return false;
}
@@ -338,7 +496,7 @@ bool Gfx::CModelFile::ReadModel(std::istream &stream, bool edit, bool meta)
}
else if ( header.revision == 1 && header.version == 1 )
{
- for (int i = 0; i < header.totalVertices; ++i)
+ for (int i = 0; i < header.totalTriangles; ++i)
{
OldModelTriangle2 t;
t.used = IOUtils::ReadBinary<1, char>(stream);
@@ -361,7 +519,7 @@ bool Gfx::CModelFile::ReadModel(std::istream &stream, bool edit, bool meta)
if (! stream.good())
{
- m_error = "Error reading model data";
+ GetLogger()->Error("Error reading model data\n");
return false;
}
@@ -381,9 +539,9 @@ bool Gfx::CModelFile::ReadModel(std::istream &stream, bool edit, bool meta)
}
else
{
- for (int i = 0; i < header.totalVertices; ++i)
+ for (int i = 0; i < header.totalTriangles; ++i)
{
- NewModelTriangle t;
+ OldModelTriangle3 t;
t.used = IOUtils::ReadBinary<1, char>(stream);
t.selected = IOUtils::ReadBinary<1, char>(stream);
@@ -406,7 +564,7 @@ bool Gfx::CModelFile::ReadModel(std::istream &stream, bool edit, bool meta)
if (! stream.good())
{
- m_error = "Error reading model data";
+ GetLogger()->Error("Error reading model data\n");
return false;
}
@@ -417,15 +575,26 @@ bool Gfx::CModelFile::ReadModel(std::istream &stream, bool edit, bool meta)
triangle.material = t.material;
triangle.tex1Name = std::string(t.texName);
- char tex2Name[20] = { 0 };
triangle.min = t.min;
triangle.max = t.max;
triangle.state = t.state;
+ triangle.variableTex2 = t.texNum2 == 1;
+
+ if (triangle.tex1Name == "plant.png")
+ triangle.state |= Gfx::ENG_RSTATE_ALPHA;
+
+ if (!triangle.variableTex2 && t.texNum2 != 0)
+ {
+ if (t.texNum2 >= 1 && t.texNum2 <= 10)
+ triangle.state |= Gfx::ENG_RSTATE_DUAL_BLACK;
- if (t.texNum2 != 0)
- sprintf(tex2Name, "dirty%.2d.tga", t.texNum2); // hardcoded as in the original code
+ if (t.texNum2 >= 11 && t.texNum2 <= 20)
+ triangle.state |= Gfx::ENG_RSTATE_DUAL_WHITE;
- triangle.tex2Name = std::string(tex2Name);
+ char tex2Name[20] = { 0 };
+ sprintf(tex2Name, "dirty%.2d.png", t.texNum2); // hardcoded as in original code
+ triangle.tex2Name = tex2Name;
+ }
m_triangles.push_back(triangle);
}
@@ -433,7 +602,12 @@ bool Gfx::CModelFile::ReadModel(std::istream &stream, bool edit, bool meta)
for (int i = 0; i < static_cast<int>( m_triangles.size() ); ++i)
{
- m_triangles[i].tex1Name = StrUtils::Replace(m_triangles[i].tex1Name, "bmp", "tga");
+ // 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");
GetLogger()->Info("ModelTriangle %d\n", i+1);
std::string s1 = m_triangles[i].p1.ToString();
@@ -448,82 +622,50 @@ bool Gfx::CModelFile::ReadModel(std::istream &stream, bool edit, bool meta)
std::string s = m_triangles[i].material.specular.ToString();
GetLogger()->Info(" mat: d: %s a: %s s: %s\n", d.c_str(), a.c_str(), s.c_str());
- GetLogger()->Info(" tex1: %s tex2: %s\n", m_triangles[i].tex1Name.c_str(), m_triangles[i].tex2Name.c_str());
+ GetLogger()->Info(" tex1: %s tex2: %s\n", m_triangles[i].tex1Name.c_str(),
+ m_triangles[i].variableTex2 ? "(variable)" : m_triangles[i].tex2Name.c_str());
GetLogger()->Info(" min: %.2f max: %.2f\n", m_triangles[i].min, m_triangles[i].max);
GetLogger()->Info(" state: %ld\n", m_triangles[i].state);
}
- /*
- if (! edit)
- {
- float limit[2];
- limit[0] = m_engine->RetLimitLOD(0); // frontier AB as config
- limit[1] = m_engine->RetLimitLOD(1); // frontier BC as config
-
- // Standard frontiers -> config.
- for (int i = 0; i < m_triangles.size(); ++i)
- {
- if ( m_triangles[i].min == 0.0f &&
- m_triangles[i].max == 100.0f ) // resolution A ?
- {
- m_triangles[i].max = limit[0];
- }
- else if ( m_triangles[i].min == 100.0f &&
- m_triangles[i].max == 200.0f ) // resolution B ?
- {
- m_triangles[i].min = limit[0];
- m_triangles[i].max = limit[1];
- }
- else if ( m_triangles[i].min == 200.0f &&
- m_triangles[i].max == 1000000.0f ) // resolution C ?
- {
- m_triangles[i].min = limit[1];
- }
- }
- }*/
-
return true;
}
-bool Gfx::CModelFile::WriteModel(const std::string &filename)
+bool Gfx::CModelFile::WriteModel(const std::string& fileName)
{
- m_error = "";
-
std::ofstream stream;
- stream.open(filename.c_str(), std::ios_base::out | std::ios_base::binary);
+ stream.open(fileName.c_str(), std::ios_base::out | std::ios_base::binary);
if (! stream.good())
{
- m_error = std::string("Could not open file '") + filename + std::string("'");
+ GetLogger()->Error("Could not open file '%s'\n", fileName.c_str());
return false;
}
return WriteModel(stream);
}
-bool Gfx::CModelFile::WriteModel(std::ostream &stream)
+bool Gfx::CModelFile::WriteModel(std::ostream& stream)
{
- m_error = "";
-
if (m_triangles.size() == 0)
{
- m_error = "Empty model";
+ GetLogger()->Error("Empty model\n");
return false;
}
- ModelHeader header;
+ OldModelHeader header;
header.revision = 1;
header.version = 2;
- header.totalVertices = m_triangles.size();
+ header.totalTriangles = m_triangles.size();
IOUtils::WriteBinary<4, int>(header.revision, stream);
IOUtils::WriteBinary<4, int>(header.version, stream);
- IOUtils::WriteBinary<4, int>(header.totalVertices, 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<int>( m_triangles.size() ); ++i)
{
- NewModelTriangle t;
+ OldModelTriangle3 t;
t.used = true;
@@ -536,14 +678,21 @@ bool Gfx::CModelFile::WriteModel(std::ostream &stream)
t.min = m_triangles[i].min;
t.max = m_triangles[i].max;
t.state = m_triangles[i].state;
+
int no = 0;
- sscanf(m_triangles[i].tex2Name.c_str(), "dirty%d.tga", &no); // hardcoded as in the original code
+ 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);
@@ -563,215 +712,481 @@ bool Gfx::CModelFile::WriteModel(std::ostream &stream)
return true;
}
-bool Gfx::CModelFile::ReadDXF(const std::string &filename, float min, float max)
+
+/*******************************************************
+ New formats
+ *******************************************************/
+
+/**
+ * \struct NewModelHeader
+ * \brief Header for new binary model file
+ */
+struct NewModelHeader
{
- m_triangles.clear();
- m_error = "";
+ //! 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
+ Gfx::VertexTex2 p1;
+ //! 2nd vertex
+ Gfx::VertexTex2 p2;
+ //! 3rd vertex
+ Gfx::VertexTex2 p3;
+ //! Material
+ Gfx::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;
+ //! Min LOD threshold
+ float min;
+ //! Max LOD threshold
+ float max;
+ //! Rendering state to be set
+ int state;
+
+ NewModelTriangle1()
+ {
+ variableTex2 = true;
+ min = max = 0.0f;
+ state = 0;
+ }
+};
+
+
+bool Gfx::CModelFile::ReadTextModel(const std::string& fileName)
+{
std::ifstream stream;
- stream.open(filename.c_str(), std::ios_base::in);
+ stream.open(fileName.c_str(), std::ios_base::in);
if (! stream.good())
{
- m_error = std::string("Couldn't open file '") + filename + std::string("'");
+ GetLogger()->Error("Could not open file '%s'\n", fileName.c_str());
return false;
}
- return ReadDXF(stream, min, max);
+ return ReadTextModel(stream);
}
-bool Gfx::CModelFile::ReadDXF(std::istream &stream, float min, float max)
+bool Gfx::CModelFile::ReadTextModel(std::istream& stream)
{
m_triangles.clear();
- m_error = "";
- if (! stream.good())
+ NewModelHeader header;
+
+ bool headOk = ReadLineValue<int>(stream, "version", header.version) &&
+ ReadLineValue<int>(stream, "total_triangles", header.totalTriangles);
+
+ if (!headOk || !stream.good())
{
- m_error = "Invalid stream";
+ GetLogger()->Error("Error reading model file header\n");
return false;
}
- // Input state
- bool waitNumVertex = false;
- bool waitNumFace = false;
- bool waitVertexX = false;
- bool waitVertexY = false;
- bool waitVertexZ = false;
- bool waitFaceX = false;
- bool waitFaceY = false;
- bool waitFaceZ = false;
-
- // Vertex array
- std::vector<Math::Vector> vertices;
- vertices.reserve(TRIANGLE_PREALLOCATE_COUNT);
-
- // Number of vertices & faces of the primitive to be read
- int vertexNum = 0, faceNum = 0;
- // Vertex coords
- Math::Vector coords;
- // Indexes of face (triangle) points
- int p1 = 0, p2 = 0, p3 = 0;
-
- // Input line
- std::string line;
- while (! stream.eof() )
+ // New model version 1
+ if (header.version == 1)
{
- // Read line with command
- std::getline(stream, line);
- int command = StrUtils::FromString<int>(line);
+ 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) &&
+ ReadLineString(stream, "p2", p2Text) &&
+ ReadLineString(stream, "p3", p3Text) &&
+ ReadLineString(stream, "mat", matText) &&
+ ReadLineValue<std::string>(stream, "tex1", t.tex1Name) &&
+ ReadLineValue<std::string>(stream, "tex2", t.tex2Name) &&
+ ReadLineValue<char>(stream, "var_tex2", varTex2Ch) &&
+ ReadLineValue<float>(stream, "min", t.min) &&
+ ReadLineValue<float>(stream, "max", t.max);
+
+ if (!triOk || !stream.good())
+ {
+ GetLogger()->Error("Error reading model file header\n");
+ return false;
+ }
- // Read line with param
- std::getline(stream, line);
+ t.p1 = ReadTextVertexTex2(p1Text);
+ t.p2 = ReadTextVertexTex2(p2Text);
+ t.p3 = ReadTextVertexTex2(p3Text);
+ t.material = ReadTextMaterial(matText);
+ t.variableTex2 = varTex2Ch == 'Y';
- bool ok = true;
+ Gfx::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.min = t.min;
+ triangle.max = t.max;
- if (command == 66)
- {
- waitNumVertex = true;
- }
+ m_triangles.push_back(triangle);
- if ( command == 71 && waitNumVertex )
- {
- waitNumVertex = false;
- vertexNum = StrUtils::FromString<int>(line, &ok);
- waitNumFace = true;
+ continue;
}
+ }
+ else
+ {
+ GetLogger()->Error("Unknown model file version\n");
+ return false;
+ }
- if ( command == 72 && waitNumFace )
- {
- waitNumFace = false;
- faceNum = StrUtils::FromString<int>(line, &ok);
- waitVertexX = true;
- }
+ for (int i = 0; i < static_cast<int>( m_triangles.size() ); ++i)
+ {
+ GetLogger()->Info("ModelTriangle %d\n", i+1);
+ std::string s1 = m_triangles[i].p1.ToString();
+ GetLogger()->Info(" p1: %s\n", s1.c_str());
+ std::string s2 = m_triangles[i].p2.ToString();
+ GetLogger()->Info(" p2: %s\n", s2.c_str());
+ std::string s3 = m_triangles[i].p3.ToString();
+ GetLogger()->Info(" p3: %s\n", s3.c_str());
- if ( command == 10 && waitVertexX )
- {
- waitVertexX = false;
- coords.x = StrUtils::FromString<float>(line, &ok);
- waitVertexY = true;
- }
+ 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()->Info(" mat: d: %s a: %s s: %s\n", d.c_str(), a.c_str(), s.c_str());
+
+ GetLogger()->Info(" tex1: %s tex2: %s\n", m_triangles[i].tex1Name.c_str(), m_triangles[i].tex2Name.c_str());
+ GetLogger()->Info(" min: %.2f max: %.2f\n", m_triangles[i].min, m_triangles[i].max);
+ GetLogger()->Info(" state: %ld\n", m_triangles[i].state);
+ }
+
+ return true;
+}
+
+bool Gfx::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 Gfx::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;
- if ( command == 20 && waitVertexY )
+ for (int i = 0; i < static_cast<int>( 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.min = m_triangles[i].min;
+ t.max = m_triangles[i].max;
+
+ 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 << "min " << t.min << std::endl;
+ stream << "max " << t.max << std::endl;
+
+ stream << std::endl;
+
+ if (! stream.good())
{
- waitVertexY = false;
- coords.y = StrUtils::FromString<float>(line, &ok);
- waitVertexZ = true;
+ GetLogger()->Error("Error writing model file\n");
+ return false;
}
+ }
+
+ return true;
+}
+
+bool Gfx::CModelFile::ReadBinaryModel(const std::string& fileName)
+{
+ std::ifstream stream;
+ stream.open(fileName.c_str(), std::ios_base::in | std::ios_base::binary);
+ if (! stream.good())
+ {
+ GetLogger()->Error("Could not open file '%s'\n", fileName.c_str());
+ return false;
+ }
+
+ return ReadBinaryModel(stream);
+}
+
+bool Gfx::CModelFile::ReadBinaryModel(std::istream& stream)
+{
+ m_triangles.clear();
- if ( command == 30 && waitVertexZ )
+ 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)
{
- waitVertexZ = false;
- coords.z = StrUtils::FromString<float>(line, &ok);
+ NewModelTriangle1 t;
- vertexNum --;
- if ( vertexNum >= 0 )
- {
- Math::Vector p(coords.x, coords.z, coords.y); // permutation of Y and Z!
- vertices.push_back(p);
- waitVertexX = true;
- }
- else
+ t.p1 = ReadBinaryVertexTex2(stream);
+ t.p2 = ReadBinaryVertexTex2(stream);
+ t.p3 = ReadBinaryVertexTex2(stream);
+ t.material = ReadBinaryMaterial(stream);
+ t.tex1Name = IOUtils::ReadBinaryString<1>(stream);
+ t.tex2Name = IOUtils::ReadBinaryString<1>(stream);
+ t.variableTex2 = IOUtils::ReadBinaryBool(stream);
+ t.min = IOUtils::ReadBinaryFloat(stream);
+ t.max = IOUtils::ReadBinaryFloat(stream);
+
+ if (! stream.good())
{
- waitFaceX = true;
+ GetLogger()->Error("Error reading model data\n");
+ return false;
}
- }
- if ( command == 71 && waitFaceX )
- {
- waitFaceX = false;
- p1 = StrUtils::FromString<int>(line, &ok);
- if ( p1 < 0 ) p1 = -p1;
- waitFaceY = true;
- }
+ Gfx::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.min = t.min;
+ triangle.max = t.max;
- if ( command == 72 && waitFaceY )
- {
- waitFaceY = false;
- p2 = StrUtils::FromString<int>(line, &ok);
- if ( p2 < 0 ) p2 = -p2;
- waitFaceZ = true;
+ m_triangles.push_back(triangle);
}
+ }
+ else
+ {
+ GetLogger()->Error("Unknown model file version\n");
+ return false;
+ }
- if ( command == 73 && waitFaceZ )
- {
- waitFaceZ = false;
- p3 = StrUtils::FromString<int>(line, &ok);
- if ( p3 < 0 ) p3 = -p3;
+ for (int i = 0; i < static_cast<int>( m_triangles.size() ); ++i)
+ {
+ GetLogger()->Info("ModelTriangle %d\n", i+1);
+ std::string s1 = m_triangles[i].p1.ToString();
+ GetLogger()->Info(" p1: %s\n", s1.c_str());
+ std::string s2 = m_triangles[i].p2.ToString();
+ GetLogger()->Info(" p2: %s\n", s2.c_str());
+ std::string s3 = m_triangles[i].p3.ToString();
+ GetLogger()->Info(" p3: %s\n", s3.c_str());
- faceNum --;
- if ( faceNum >= 0 )
- {
- assert( (p1-1 >= 0) && (p1-1 < static_cast<int>(vertices.size())) );
- assert( (p2-1 >= 0) && (p2-1 < static_cast<int>(vertices.size())) );
- assert( (p3-1 >= 0) && (p3-1 < static_cast<int>(vertices.size())) );
+ 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()->Info(" mat: d: %s a: %s s: %s\n", d.c_str(), a.c_str(), s.c_str());
- CreateTriangle(vertices[p3-1], vertices[p2-1], vertices[p1-1], min, max);
- waitFaceX = true;
- }
- }
+ GetLogger()->Info(" tex1: %s tex2: %s\n", m_triangles[i].tex1Name.c_str(), m_triangles[i].tex2Name.c_str());
+ GetLogger()->Info(" min: %.2f max: %.2f\n", m_triangles[i].min, m_triangles[i].max);
+ GetLogger()->Info(" state: %ld\n", m_triangles[i].state);
+ }
+
+ return true;
+}
+
+bool Gfx::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 Gfx::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();
- if (! ok)
+ IOUtils::WriteBinary<4, int>(header.version, stream);
+ IOUtils::WriteBinary<4, int>(header.totalTriangles, stream);
+
+ for (int i = 0; i < static_cast<int>( 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.min = m_triangles[i].min;
+ t.max = m_triangles[i].max;
+
+ 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::WriteBinaryFloat(t.min, stream);
+ IOUtils::WriteBinaryFloat(t.max, stream);
+
+ if (! stream.good())
{
- m_error = "Error reading data";
+ GetLogger()->Error("Error writing model file\n");
return false;
}
-
}
return true;
}
-bool Gfx::CModelFile::CreateEngineObject(int objRank, int addState)
+
+/*******************************************************
+ Other stuff
+ *******************************************************/
+
+#ifndef MODELFILE_NO_ENGINE
+
+bool Gfx::CModelFile::CreateEngineObject(int objRank)
{
+ std::vector<Gfx::VertexTex2> vs(3, Gfx::VertexTex2());
+
+ float limit[2];
+ limit[0] = m_engine->GetLimitLOD(0); // frontier AB as config
+ limit[1] = m_engine->GetLimitLOD(1); // frontier BC as config
+
for (int i = 0; i < static_cast<int>( m_triangles.size() ); i++)
{
- int state = m_triangles[i].state;
+ // TODO move this to CEngine
+
+ float min = m_triangles[i].min;
+ float max = m_triangles[i].max;
+
+ // Standard frontiers -> config
+ if (min == 0.0f && max == 100.0f) // resolution A ?
+ {
+ max = limit[0];
+ }
+ else if (min == 100.0f && max == 200.0f) // resolution B ?
+ {
+ min = limit[0];
+ max = limit[1];
+ }
+ else if (min == 200.0f && max == 1000000.0f) // resolution C ?
+ {
+ min = limit[1];
+ }
- /* TODO ???
- if (texName1 == "plant.png")
- state |= Gfx::ENG_RSTATE_ALPHA;
+ int state = m_triangles[i].state;
+ std::string tex2Name = m_triangles[i].tex2Name;
- if (m_triangles[i].tex2Name.empty())
+ if (m_triangles[i].variableTex2)
{
- int texNum = 0;
+ int texNum = m_engine->GetSecondTexture();
- if ( m_triangles[i].texNum2 == 1 )
- {
- texNum = m_engine->RetSecondTexture();
- }
- else
- {
- texNum = m_triangles[i].texNum2;
- }
+ if (texNum >= 1 && texNum <= 10)
+ state |= Gfx::ENG_RSTATE_DUAL_BLACK;
- if ( texNum >= 1 && texNum <= 10 )
- {
- state |= D3DSTATEDUALb;
- }
- if ( texNum >= 11 && texNum <= 20 )
- {
- state |= D3DSTATEDUALw;
- }
- sprintf(texName2, "dirty%.2d.tga", texNum); // ???
- }*/
+ if (texNum >= 11 && texNum <= 20)
+ state |= Gfx::ENG_RSTATE_DUAL_WHITE;
- std::vector<Gfx::VertexTex2> vs;
- vs.push_back(m_triangles[i].p1);
- vs.push_back(m_triangles[i].p2);
- vs.push_back(m_triangles[i].p3);
+ char name[20] = { 0 };
+ sprintf(name, "dirty%.2d.png", texNum);
+ tex2Name = name;
+ }
- m_engine->AddTriangles(objRank, vs,
- m_triangles[i].material,
- state + addState,
- m_triangles[i].tex1Name,
- m_triangles[i].tex2Name,
- m_triangles[i].min,
- m_triangles[i].max, false);
+ vs[0] = m_triangles[i].p1;
+ vs[1] = m_triangles[i].p2;
+ vs[2] = m_triangles[i].p3;
+
+ bool ok = m_engine->AddTriangles(objRank, vs,
+ m_triangles[i].material,
+ state,
+ m_triangles[i].tex1Name,
+ tex2Name,
+ min, max, false);
+ if (!ok)
+ return false;
}
return true;
}
+#endif
+
void Gfx::CModelFile::Mirror()
{
for (int i = 0; i < static_cast<int>( m_triangles.size() ); i++)
@@ -790,7 +1205,7 @@ void Gfx::CModelFile::Mirror()
}
}
-std::vector<Gfx::ModelTriangle>& Gfx::CModelFile::GetTriangles()
+const std::vector<Gfx::ModelTriangle>& Gfx::CModelFile::GetTriangles()
{
return m_triangles;
}
diff --git a/src/graphics/engine/modelfile.h b/src/graphics/engine/modelfile.h
index fab190f..833cdf6 100644
--- a/src/graphics/engine/modelfile.h
+++ b/src/graphics/engine/modelfile.h
@@ -20,7 +20,6 @@
* \brief Model loading - Gfx::CModelFile class (aka modfile)
*/
-#include "graphics/engine/engine.h"
#include "graphics/core/vertex.h"
#include "graphics/core/material.h"
#include "math/vector.h"
@@ -35,6 +34,9 @@ class CInstanceManager;
namespace Gfx {
+class CEngine;
+
+
/**
\struct ModelTriangle
\brief Triangle of a 3D model
@@ -53,14 +55,21 @@ struct ModelTriangle
std::string tex1Name;
//! Name of 2nd texture
std::string tex2Name;
+ //! If true, 2nd texture will be taken from current engine setting
+ bool variableTex2;
//! Min LOD threshold
float min;
//! Max LOD threshold
float max;
//! Rendering state to be set
- long state;
-
- ModelTriangle();
+ int state;
+
+ ModelTriangle()
+ {
+ variableTex2 = true;
+ min = max = 0.0f;
+ state = 0;
+ }
};
@@ -75,27 +84,45 @@ public:
CModelFile(CInstanceManager* iMan);
~CModelFile();
- //! Returns the last error encountered
- std::string GetError();
+ //! 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
- bool ReadModel(const std::string &filename, bool edit = false, bool meta = true);
+ //! @deprecated
+ bool ReadModel(const std::string &fileName);
//! Reads a binary Colobot model from stream
- bool ReadModel(std::istream &stream, bool edit = false, bool meta = true);
+ //! @deprecated
+ bool ReadModel(std::istream &stream);
//! Writes the model to Colobot binary model file
- bool WriteModel(const std::string &filename);
+ //! @deprecated
+ bool WriteModel(const std::string &fileName);
//! Writes the model to Colobot binary model file
+ //! @deprecated
bool WriteModel(std::ostream &stream);
- //! Reads a DXF model from file
- bool ReadDXF(const std::string &filename, float min, float max);
- //! Reads a DXF model from stream
- bool ReadDXF(std::istream &stream, float min, float max);
-
//! Returns the number of triangles in model
int GetTriangleCount();
+
//! Returns the triangle vector
- std::vector<Gfx::ModelTriangle>& GetTriangles();
+ const std::vector<Gfx::ModelTriangle>& GetTriangles();
+
//! Returns the height of model -- closest point to X and Z coords of \a pos
float GetHeight(Math::Vector pos);
@@ -103,7 +130,7 @@ public:
void Mirror();
//! Creates an object in the graphics engine from the model
- bool CreateEngineObject(int objRank, int addState = 0);
+ bool CreateEngineObject(int objRank);
protected:
//! Adds a triangle to the list
@@ -113,9 +140,6 @@ protected:
CInstanceManager* m_iMan;
Gfx::CEngine* m_engine;
- //! Last error
- std::string m_error;
-
//! Model triangles
std::vector<Gfx::ModelTriangle> m_triangles;
};
diff --git a/src/graphics/engine/terrain.cpp b/src/graphics/engine/terrain.cpp
index 368796a..971eed1 100644
--- a/src/graphics/engine/terrain.cpp
+++ b/src/graphics/engine/terrain.cpp
@@ -180,7 +180,7 @@ void Gfx::CTerrain::LevelFlush()
LevelCloseTable();
}
-void Gfx::CTerrain::LevelMaterial(int id, std::string& baseName, float u, float v,
+void Gfx::CTerrain::LevelMaterial(int id, const std::string& baseName, float u, float v,
int up, int right, int down, int left,
float hardness)
{
diff --git a/src/graphics/engine/terrain.h b/src/graphics/engine/terrain.h
index 24bd1f9..0c3fa63 100644
--- a/src/graphics/engine/terrain.h
+++ b/src/graphics/engine/terrain.h
@@ -157,7 +157,7 @@ public:
//! Empties level
void LevelFlush();
//! Initializes the names of textures to use for the land
- void LevelMaterial(int id, std::string& baseName, float u, float v, int up, int right, int down, int left, float hardness);
+ void LevelMaterial(int id, const std::string& baseName, float u, float v, int up, int right, int down, int left, float hardness);
//! Initializes all the ground with a material
bool LevelInit(int id);
//! Generates a level in the terrain
diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt
new file mode 100644
index 0000000..f6c6112
--- /dev/null
+++ b/src/tools/CMakeLists.txt
@@ -0,0 +1,13 @@
+set(CONVERT_MODEL_SOURCES
+../common/iman.cpp
+../common/logger.cpp
+../common/stringutils.cpp
+../graphics/engine/modelfile.cpp
+convert_model.cpp
+)
+
+include_directories(. ..)
+
+add_definitions(-DMODELFILE_NO_ENGINE)
+
+add_executable(convert_model ${CONVERT_MODEL_SOURCES})
diff --git a/src/tools/README.txt b/src/tools/README.txt
new file mode 100644
index 0000000..de2f087
--- /dev/null
+++ b/src/tools/README.txt
@@ -0,0 +1,4 @@
+/**
+ * \dir tools
+ * \brief Various tools (separate programs)
+ */
diff --git a/src/tools/convert_model.cpp b/src/tools/convert_model.cpp
new file mode 100644
index 0000000..472f1c6
--- /dev/null
+++ b/src/tools/convert_model.cpp
@@ -0,0 +1,285 @@
+#include "common/iman.h"
+#include "common/logger.h"
+#include "graphics/engine/modelfile.h"
+
+#include <iostream>
+#include <map>
+
+
+bool EndsWith(std::string const &fullString, std::string const &ending)
+{
+ if (fullString.length() >= ending.length()) {
+ return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
+ } else {
+ return false;
+ }
+}
+
+
+struct Args
+{
+ bool usage;
+ bool dumpInfo;
+ bool mirror;
+ std::string inputFile;
+ std::string outputFile;
+ std::string inputFormat;
+ std::string outputFormat;
+
+ Args()
+ {
+ usage = false;
+ dumpInfo = false;
+ mirror = false;
+ }
+};
+
+Args ARGS;
+
+void PrintUsage(const std::string& program)
+{
+ std::cerr << "Colobot model converter" << std::endl;
+ std::cerr << std::endl;
+ std::cerr << "Usage:" << std::endl;
+ std::cerr << std::endl;
+ std::cerr << " Convert files:" << std::endl;
+ std::cerr << " " << program << " -i input_file -if input_format -o output_file -of output_format [-m]" << std::endl;
+ std::cerr << " -m => mirror" << std::endl;
+ std::cerr << std::endl;
+ std::cerr << " Dump info:" << std::endl;
+ std::cerr << " " << program << " -d -i input_file -if input_format" << std::endl;
+ std::cerr << std::endl;
+ std::cerr << " Help:" << std::endl;
+ std::cerr << " " << program << " -h" << std::endl;
+ std::cerr << std::endl;
+
+ std::cerr << "Model formats:" << std::endl;
+ std::cerr << " old => old binary format" << std::endl;
+ std::cerr << " new_bin => new binary format" << std::endl;
+ std::cerr << " new_txt => new text format" << std::endl;
+}
+
+bool ParseArgs(int argc, char *argv[])
+{
+ bool waitI = false, waitO = false;
+ bool waitIf = false, waitOf = false;
+ for (int i = 1; i < argc; ++i)
+ {
+ std::string arg = std::string(argv[i]);
+
+ if (arg == "-i")
+ {
+ waitI = true;
+ continue;
+ }
+ if (arg == "-o")
+ {
+ waitO = true;
+ continue;
+ }
+ if (arg == "-if")
+ {
+ waitIf = true;
+ continue;
+ }
+ if (arg == "-of")
+ {
+ waitOf = true;
+ continue;
+ }
+
+ if (waitI)
+ {
+ waitI = false;
+ ARGS.inputFile = arg;
+ }
+ else if (waitO)
+ {
+ waitO = false;
+ ARGS.outputFile = arg;
+ }
+ else if (waitIf)
+ {
+ waitIf = false;
+ ARGS.inputFormat = arg;
+ }
+ else if (waitOf)
+ {
+ waitOf = false;
+ ARGS.outputFormat = arg;
+ }
+ else if (arg == "-h")
+ {
+ PrintUsage(argv[0]);
+ ARGS.usage = true;
+ }
+ else if (arg == "-d")
+ {
+ ARGS.dumpInfo = true;
+ }
+ else if (arg == "-m")
+ {
+ ARGS.mirror = true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ if (waitI || waitO || waitIf || waitOf)
+ return false;
+
+ if (ARGS.usage)
+ return true;
+
+ if (ARGS.inputFile.empty() || (!ARGS.dumpInfo && ARGS.outputFile.empty() ))
+ return false;
+
+ if (ARGS.inputFormat.empty() || (!ARGS.dumpInfo && ARGS.outputFormat.empty() ))
+ return false;
+
+ return true;
+}
+
+template<typename T>
+void PrintStats(const std::map<T, int>& stats, int total)
+{
+ for (auto it = stats.begin(); it != stats.end(); ++it)
+ {
+ std::cerr << " " << (*it).first << " : " << (*it).second << " / " << total << std::endl;
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ CLogger logger;
+
+ if (!ParseArgs(argc, argv))
+ {
+ std::cerr << "Invalid arguments! Run with -h for usage info." << std::endl;
+ return 1;
+ }
+
+ if (ARGS.usage)
+ return 0;
+
+ CInstanceManager iMan;
+ Gfx::CModelFile model(&iMan);
+
+ bool ok = true;
+
+ if (ARGS.inputFormat == "old")
+ {
+ ok = model.ReadModel(ARGS.inputFile);
+ }
+ else if (ARGS.inputFormat == "new_bin")
+ {
+ ok = model.ReadBinaryModel(ARGS.inputFile);
+ }
+ else if (ARGS.inputFormat == "new_txt")
+ {
+ ok = model.ReadTextModel(ARGS.inputFile);
+ }
+ else
+ {
+ std::cerr << "Invalid input format" << std::endl;
+ return 1;
+ }
+
+ if (!ok)
+ {
+ std::cerr << "Reading input model failed" << std::endl;
+ return 1;
+ }
+
+ if (ARGS.dumpInfo)
+ {
+ const std::vector<Gfx::ModelTriangle>& triangles = model.GetTriangles();
+
+ Math::Vector min( Math::HUGE_NUM, Math::HUGE_NUM, Math::HUGE_NUM);
+ Math::Vector max(-Math::HUGE_NUM, -Math::HUGE_NUM, -Math::HUGE_NUM);
+
+ std::map<std::string, int> texs1, texs2;
+ std::map<int, int> states;
+ std::map<float, int> mins, maxs;
+ int variableTexs2 = 0;
+
+ for (int i = 0; i < static_cast<int>( triangles.size() ); ++i)
+ {
+ const Gfx::ModelTriangle& t = triangles[i];
+
+ min.x = Math::Min(t.p1.coord.x, t.p2.coord.x, t.p3.coord.x, min.x);
+ min.y = Math::Min(t.p1.coord.y, t.p2.coord.y, t.p3.coord.y, min.y);
+ min.z = Math::Min(t.p1.coord.z, t.p2.coord.z, t.p3.coord.z, min.z);
+
+ max.x = Math::Max(t.p1.coord.x, t.p2.coord.x, t.p3.coord.x, max.x);
+ max.y = Math::Max(t.p1.coord.y, t.p2.coord.y, t.p3.coord.y, max.y);
+ max.z = Math::Max(t.p1.coord.z, t.p2.coord.z, t.p3.coord.z, max.z);
+
+ texs1[t.tex1Name] += 1;
+ if (! t.tex2Name.empty())
+ texs2[t.tex2Name] += 1;
+ if (t.variableTex2)
+ variableTexs2 += 1;
+ states[t.state] += 1;
+
+ mins[t.min] += 1;
+ maxs[t.max] += 1;
+ }
+
+ std::cerr << "---- Info ----" << std::endl;
+ std::cerr << "Total triangles: " << triangles.size();
+ std::cerr << std::endl;
+ std::cerr << "Bounding box:" << std::endl;
+ std::cerr << " min: [" << min.x << ", " << min.y << ", " << min.z << "]" << std::endl;
+ std::cerr << " max: [" << max.x << ", " << max.y << ", " << max.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;
+ std::cerr << " min:" << std::endl;
+ PrintStats(mins, triangles.size());
+ std::cerr << " max:" << std::endl;
+ PrintStats(maxs, triangles.size());
+
+ return 0;
+ }
+
+ if (ARGS.mirror)
+ model.Mirror();
+
+ if (ARGS.outputFormat == "old")
+ {
+ ok = model.WriteModel(ARGS.outputFile);
+ }
+ else if (ARGS.outputFormat == "new_bin")
+ {
+ ok = model.WriteBinaryModel(ARGS.outputFile);
+ }
+ else if (ARGS.outputFormat == "new_txt")
+ {
+ ok = model.WriteTextModel(ARGS.outputFile);
+ }
+ else
+ {
+ std::cerr << "Invalid output format" << std::endl;
+ return 1;
+ }
+
+ if (!ok)
+ {
+ std::cerr << "Writing output model failed" << std::endl;
+ return 1;
+ }
+
+ return 0;
+}