colobot/src/graphics/engine/modelfile.cpp

1250 lines
36 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsiteс.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#include "graphics/engine/modelfile.h"
#include "common/ioutils.h"
#include "common/logger.h"
#include "common/stringutils.h"
#ifndef MODELFILE_NO_ENGINE
#include "common/resources/inputstream.h"
#endif
#include "graphics/engine/engine.h"
#include "math/geometry.h"
#include <cstring>
#include <fstream>
#include <sstream>
// Graphics module namespace
namespace Gfx {
bool ReadBinaryVertex(std::istream& stream, Vertex& vertex)
{
vertex.coord.x = IOUtils::ReadBinaryFloat(stream);
vertex.coord.y = IOUtils::ReadBinaryFloat(stream);
vertex.coord.z = IOUtils::ReadBinaryFloat(stream);
vertex.normal.x = IOUtils::ReadBinaryFloat(stream);
vertex.normal.y = IOUtils::ReadBinaryFloat(stream);
vertex.normal.z = IOUtils::ReadBinaryFloat(stream);
vertex.texCoord.x = IOUtils::ReadBinaryFloat(stream);
vertex.texCoord.y = IOUtils::ReadBinaryFloat(stream);
return !stream.fail();
}
bool WriteBinaryVertex(Vertex vertex, std::ostream& stream)
{
IOUtils::WriteBinaryFloat(vertex.coord.x, stream);
IOUtils::WriteBinaryFloat(vertex.coord.y, stream);
IOUtils::WriteBinaryFloat(vertex.coord.z, stream);
IOUtils::WriteBinaryFloat(vertex.normal.x, stream);
IOUtils::WriteBinaryFloat(vertex.normal.y, stream);
IOUtils::WriteBinaryFloat(vertex.normal.z, stream);
IOUtils::WriteBinaryFloat(vertex.texCoord.x, stream);
IOUtils::WriteBinaryFloat(vertex.texCoord.y, stream);
return !stream.fail();
}
bool ReadBinaryVertexTex2(std::istream& stream, VertexTex2& vertex)
{
vertex.coord.x = IOUtils::ReadBinaryFloat(stream);
vertex.coord.y = IOUtils::ReadBinaryFloat(stream);
vertex.coord.z = IOUtils::ReadBinaryFloat(stream);
vertex.normal.x = IOUtils::ReadBinaryFloat(stream);
vertex.normal.y = IOUtils::ReadBinaryFloat(stream);
vertex.normal.z = IOUtils::ReadBinaryFloat(stream);
vertex.texCoord.x = IOUtils::ReadBinaryFloat(stream);
vertex.texCoord.y = IOUtils::ReadBinaryFloat(stream);
vertex.texCoord2.x = IOUtils::ReadBinaryFloat(stream);
vertex.texCoord2.y = IOUtils::ReadBinaryFloat(stream);
return !stream.fail();
}
bool WriteBinaryVertexTex2(VertexTex2 vertex, std::ostream& stream)
{
IOUtils::WriteBinaryFloat(vertex.coord.x, stream);
IOUtils::WriteBinaryFloat(vertex.coord.y, stream);
IOUtils::WriteBinaryFloat(vertex.coord.z, stream);
IOUtils::WriteBinaryFloat(vertex.normal.x, stream);
IOUtils::WriteBinaryFloat(vertex.normal.y, stream);
IOUtils::WriteBinaryFloat(vertex.normal.z, stream);
IOUtils::WriteBinaryFloat(vertex.texCoord.x, stream);
IOUtils::WriteBinaryFloat(vertex.texCoord.y, stream);
IOUtils::WriteBinaryFloat(vertex.texCoord2.x, stream);
IOUtils::WriteBinaryFloat(vertex.texCoord2.y, stream);
return !stream.fail();
}
bool ReadTextVertexTex2(const std::string& text, VertexTex2& vertex)
{
std::stringstream stream;
stream.str(text);
std::string what;
stream >> what;
if (what != "c")
return false;
stream >> vertex.coord.x >> vertex.coord.y >> vertex.coord.z;
stream >> what;
if (what != "n")
return false;
stream >> vertex.normal.x >> vertex.normal.y >> vertex.normal.z;
stream >> what;
if (what != "t1")
return false;
stream >> vertex.texCoord.x >> vertex.texCoord.y;
stream >> what;
if (what != "t2")
return false;
stream >> vertex.texCoord2.x >> vertex.texCoord2.y;
return !stream.fail();
}
bool WriteTextVertexTex2(const VertexTex2& vertex, std::ostream& stream)
{
stream << "c " << vertex.coord.x << " " << vertex.coord.y << " " << vertex.coord.z;
stream << " n " << vertex.normal.x << " " << vertex.normal.y << " " << vertex.normal.z;
stream << " t1 " << vertex.texCoord.x << " " << vertex.texCoord.y;
stream << " t2 " << vertex.texCoord2.x << " " << vertex.texCoord2.y;
stream << std::endl;
return !stream.fail();
}
bool ReadBinaryMaterial(std::istream& stream, Material& material)
{
material.diffuse.r = IOUtils::ReadBinaryFloat(stream);
material.diffuse.g = IOUtils::ReadBinaryFloat(stream);
material.diffuse.b = IOUtils::ReadBinaryFloat(stream);
material.diffuse.a = IOUtils::ReadBinaryFloat(stream);
material.ambient.r = IOUtils::ReadBinaryFloat(stream);
material.ambient.g = IOUtils::ReadBinaryFloat(stream);
material.ambient.b = IOUtils::ReadBinaryFloat(stream);
material.ambient.a = IOUtils::ReadBinaryFloat(stream);
material.specular.r = IOUtils::ReadBinaryFloat(stream);
material.specular.g = IOUtils::ReadBinaryFloat(stream);
material.specular.b = IOUtils::ReadBinaryFloat(stream);
material.specular.a = IOUtils::ReadBinaryFloat(stream);
/* emissive.r = */ IOUtils::ReadBinaryFloat(stream);
/* emissive.g = */ IOUtils::ReadBinaryFloat(stream);
/* emissive.b = */ IOUtils::ReadBinaryFloat(stream);
/* emissive.a = */ IOUtils::ReadBinaryFloat(stream);
/* power = */ IOUtils::ReadBinaryFloat(stream);
return !stream.fail();
}
bool WriteBinaryMaterial(const Material& material, std::ostream& stream)
{
IOUtils::WriteBinaryFloat(material.diffuse.r, stream);
IOUtils::WriteBinaryFloat(material.diffuse.g, stream);
IOUtils::WriteBinaryFloat(material.diffuse.b, stream);
IOUtils::WriteBinaryFloat(material.diffuse.a, stream);
IOUtils::WriteBinaryFloat(material.ambient.r, stream);
IOUtils::WriteBinaryFloat(material.ambient.g, stream);
IOUtils::WriteBinaryFloat(material.ambient.b, stream);
IOUtils::WriteBinaryFloat(material.ambient.a, stream);
IOUtils::WriteBinaryFloat(material.specular.r, stream);
IOUtils::WriteBinaryFloat(material.specular.g, stream);
IOUtils::WriteBinaryFloat(material.specular.b, stream);
IOUtils::WriteBinaryFloat(material.specular.a, stream);
/* emissive.r */ IOUtils::WriteBinaryFloat(0.0f, stream);
/* emissive.g */ IOUtils::WriteBinaryFloat(0.0f, stream);
/* emissive.b */ IOUtils::WriteBinaryFloat(0.0f, stream);
/* emissive.a */ IOUtils::WriteBinaryFloat(0.0f, stream);
/* power */ IOUtils::WriteBinaryFloat(0.0f, stream);
return !stream.fail();
}
bool ReadTextMaterial(const std::string& text, Material& material)
{
std::stringstream stream;
stream.str(text);
std::string what;
stream >> what;
if (what != "dif")
return false;
stream >> material.diffuse.r
>> material.diffuse.g
>> material.diffuse.b
>> material.diffuse.a;
stream >> what;
if (what != "amb")
return false;
stream >> material.ambient.r
>> material.ambient.g
>> material.ambient.b
>> material.ambient.a;
stream >> what;
if (what != "spc")
return false;
stream >> material.specular.r
>> material.specular.g
>> material.specular.b
>> material.specular.a;
return !stream.fail();
}
bool WriteTextMaterial(const Material& material, std::ostream& stream)
{
stream << "dif " << material.diffuse.r
<< " " << material.diffuse.g
<< " " << material.diffuse.b
<< " " << material.diffuse.a;
stream << " amb " << material.ambient.r
<< " " << material.ambient.g
<< " " << material.ambient.b
<< " " << material.ambient.a;
stream << " spc " << material.specular.r
<< " " << material.specular.g << " "
<< material.specular.b << " "
<< material.specular.a;
stream << std::endl;
return !stream.fail();
}
template<typename T>
bool ReadLineValue(std::istream& stream, const std::string& prefix, T& value)
{
std::string line;
while (true)
{
if (stream.eof() || stream.fail())
return false;
std::getline(stream, line);
if (!line.empty() && line[0] != '#')
break;
}
std::stringstream s;
s.str(line);
std::string what;
s >> what;
if (what != prefix)
return false;
s >> value;
return true;
}
bool ReadLineString(std::istream& stream, const std::string& prefix, std::string& value)
{
std::string line;
while (true)
{
if (stream.eof() || stream.fail())
return false;
std::getline(stream, line);
if (!line.empty() && line[0] != '#')
break;
}
std::stringstream s;
s.str(line);
std::string what;
s >> what;
if (what != prefix)
return false;
std::getline(s, value);
return true;
}
CModelFile::CModelFile()
: m_printDebugInfo(false)
{
}
CModelFile::~CModelFile()
{
}
/*******************************************************
Deprecated formats
*******************************************************/
/**
* \struct OldModelHeader
* \brief Colobot binary model header info
*
* @deprecated
*/
struct OldModelHeader
{
//! Revision number
int revision;
//! Version number
int version;
//! Total number of triangles
int totalTriangles;
//! Reserved area
int reserved[10];
OldModelHeader()
{
memset(this, 0, sizeof(*this));
}
};
/**
* \struct OldModelTriangle1
* \brief Colobot binary model file version 1
*
* @deprecated
*/
struct OldModelTriangle1
{
char used;
char selected;
Vertex p1;
Vertex p2;
Vertex p3;
Material material;
char texName[20];
float min;
float max;
OldModelTriangle1()
{
memset(this, 0, sizeof(*this));
}
};
/**
* \struct OldModelTriangle2
* \brief Colobot binary model file version 2
*
* @deprecated
*/
struct OldModelTriangle2
{
char used;
char selected;
Vertex p1;
Vertex p2;
Vertex p3;
Material material;
char texName[20];
float min;
float max;
long state;
short reserved1;
short reserved2;
short reserved3;
short reserved4;
OldModelTriangle2()
{
memset(this, 0, sizeof(*this));
}
};
/**
* \struct OldModelTriangle3
* \brief Colobot binary model file version 3
*
* @deprecated
*/
struct OldModelTriangle3
{
char used;
char selected;
VertexTex2 p1;
VertexTex2 p2;
VertexTex2 p3;
Material material;
char texName[20];
float min;
float max;
long state;
short texNum2;
short reserved2;
short reserved3;
short reserved4;
OldModelTriangle3()
{
memset(this, 0, sizeof(*this));
}
};
bool CModelFile::ReadModel(const std::string& fileName)
{
m_triangles.clear();
#ifndef MODELFILE_NO_ENGINE
CInputStream stream;
stream.open(fileName);
if (!stream.is_open())
{
GetLogger()->Error("Could not open file '%s'\n", fileName.c_str());
return false;
}
#else
std::ifstream stream;
stream.open(fileName);
if (!stream.good())
{
GetLogger()->Error("Could not open file '%s'\n", fileName.c_str());
return false;
}
#endif
return ReadModel(stream);
}
bool CModelFile::ReadModel(std::istream& stream)
{
m_triangles.clear();
OldModelHeader header;
header.revision = IOUtils::ReadBinary<4, int>(stream);
header.version = IOUtils::ReadBinary<4, int>(stream);
header.totalTriangles = IOUtils::ReadBinary<4, int>(stream);
for (int i = 0; i < 10; ++i)
header.reserved[i] = IOUtils::ReadBinary<4, int>(stream);
if (!stream.good())
{
GetLogger()->Error("Error reading model file header\n");
return false;
}
// Old model version #1
if ( (header.revision == 1) && (header.version == 0) )
{
for (int i = 0; i < header.totalTriangles; ++i)
{
OldModelTriangle1 t;
t.used = IOUtils::ReadBinary<1, char>(stream);
t.selected = IOUtils::ReadBinary<1, char>(stream);
/* padding */ IOUtils::ReadBinary<2, unsigned int>(stream);
ReadBinaryVertex(stream, t.p1);
ReadBinaryVertex(stream, t.p2);
ReadBinaryVertex(stream, t.p3);
ReadBinaryMaterial(stream, t.material);
stream.read(t.texName, 20);
t.min = IOUtils::ReadBinaryFloat(stream);
t.max = IOUtils::ReadBinaryFloat(stream);
if (stream.fail())
{
GetLogger()->Error("Error reading model data\n");
return false;
}
ModelTriangle triangle;
triangle.p1.FromVertex(t.p1);
triangle.p2.FromVertex(t.p2);
triangle.p3.FromVertex(t.p3);
triangle.material = t.material;
triangle.tex1Name = std::string(t.texName);
triangle.lodLevel = MinMaxToLodLevel(t.min, t.max);
m_triangles.push_back(triangle);
}
}
else if ( header.revision == 1 && header.version == 1 )
{
for (int i = 0; i < header.totalTriangles; ++i)
{
OldModelTriangle2 t;
t.used = IOUtils::ReadBinary<1, char>(stream);
t.selected = IOUtils::ReadBinary<1, char>(stream);
/* padding */ IOUtils::ReadBinary<2, unsigned int>(stream);
ReadBinaryVertex(stream, t.p1);
ReadBinaryVertex(stream, t.p2);
ReadBinaryVertex(stream, t.p3);
ReadBinaryMaterial(stream, t.material);
stream.read(t.texName, 20);
t.min = IOUtils::ReadBinaryFloat(stream);
t.max = IOUtils::ReadBinaryFloat(stream);
t.state = IOUtils::ReadBinary<4, long>(stream);
t.reserved1 = IOUtils::ReadBinary<2, short>(stream);
t.reserved2 = IOUtils::ReadBinary<2, short>(stream);
t.reserved3 = IOUtils::ReadBinary<2, short>(stream);
t.reserved4 = IOUtils::ReadBinary<2, short>(stream);
if (stream.fail())
{
GetLogger()->Error("Error reading model data\n");
return false;
}
ModelTriangle triangle;
triangle.p1.FromVertex(t.p1);
triangle.p2.FromVertex(t.p2);
triangle.p3.FromVertex(t.p3);
triangle.material = t.material;
triangle.tex1Name = std::string(t.texName);
triangle.lodLevel = MinMaxToLodLevel(t.min, t.max);
triangle.state = t.state;
m_triangles.push_back(triangle);
}
}
else
{
for (int i = 0; i < header.totalTriangles; ++i)
{
OldModelTriangle3 t;
t.used = IOUtils::ReadBinary<1, char>(stream);
t.selected = IOUtils::ReadBinary<1, char>(stream);
/* padding */ IOUtils::ReadBinary<2, unsigned int>(stream);
ReadBinaryVertexTex2(stream, t.p1);
ReadBinaryVertexTex2(stream, t.p2);
ReadBinaryVertexTex2(stream, t.p3);
ReadBinaryMaterial(stream, t.material);
stream.read(t.texName, 20);
t.min = IOUtils::ReadBinaryFloat(stream);
t.max = IOUtils::ReadBinaryFloat(stream);
t.state = IOUtils::ReadBinary<4, long>(stream);
t.texNum2 = IOUtils::ReadBinary<2, short>(stream);
t.reserved2 = IOUtils::ReadBinary<2, short>(stream);
t.reserved3 = IOUtils::ReadBinary<2, short>(stream);
t.reserved4 = IOUtils::ReadBinary<2, short>(stream);
if (stream.fail())
{
GetLogger()->Error("Error reading model data\n");
return false;
}
ModelTriangle triangle;
triangle.p1 = t.p1;
triangle.p2 = t.p2;
triangle.p3 = t.p3;
triangle.material = t.material;
triangle.tex1Name = std::string(t.texName);
triangle.lodLevel = MinMaxToLodLevel(t.min, t.max);
triangle.state = t.state;
triangle.variableTex2 = t.texNum2 == 1;
if (!triangle.variableTex2 && t.texNum2 != 0)
{
if (t.texNum2 >= 1 && t.texNum2 <= 10)
triangle.state |= ENG_RSTATE_DUAL_BLACK;
if (t.texNum2 >= 11 && t.texNum2 <= 20)
triangle.state |= ENG_RSTATE_DUAL_WHITE;
char tex2Name[20] = { 0 };
sprintf(tex2Name, "dirty%.2d.png", t.texNum2); // hardcoded as in original code
triangle.tex2Name = tex2Name;
}
m_triangles.push_back(triangle);
}
}
for (int i = 0; i < static_cast<int>( m_triangles.size() ); ++i)
{
// All extensions are now png
m_triangles[i].tex1Name = StrUtils::Replace(m_triangles[i].tex1Name, "bmp", "png");
m_triangles[i].tex1Name = StrUtils::Replace(m_triangles[i].tex1Name, "tga", "png");
m_triangles[i].tex2Name = StrUtils::Replace(m_triangles[i].tex2Name, "bmp", "png");
m_triangles[i].tex2Name = StrUtils::Replace(m_triangles[i].tex2Name, "tga", "png");
// TODO: fix this in model files
if (m_triangles[i].tex1Name == "plant.png")
m_triangles[i].state |= ENG_RSTATE_ALPHA;
if (m_printDebugInfo)
{
GetLogger()->Trace("ModelTriangle %d\n", i+1);
std::string s1 = m_triangles[i].p1.ToString();
GetLogger()->Trace(" p1: %s\n", s1.c_str());
std::string s2 = m_triangles[i].p2.ToString();
GetLogger()->Trace(" p2: %s\n", s2.c_str());
std::string s3 = m_triangles[i].p3.ToString();
GetLogger()->Trace(" p3: %s\n", s3.c_str());
std::string d = m_triangles[i].material.diffuse.ToString();
std::string a = m_triangles[i].material.ambient.ToString();
std::string s = m_triangles[i].material.specular.ToString();
GetLogger()->Trace(" mat: d: %s a: %s s: %s\n", d.c_str(), a.c_str(), s.c_str());
GetLogger()->Trace(" tex1: %s tex2: %s\n", m_triangles[i].tex1Name.c_str(),
m_triangles[i].variableTex2 ? "(variable)" : m_triangles[i].tex2Name.c_str());
GetLogger()->Trace(" lod level: %d\n", m_triangles[i].lodLevel);
GetLogger()->Trace(" state: %ld\n", m_triangles[i].state);
}
}
return true;
}
bool CModelFile::WriteModel(const std::string& fileName)
{
std::ofstream stream;
stream.open(fileName.c_str(), std::ios_base::out | std::ios_base::binary);
if (!stream.good())
{
GetLogger()->Error("Could not open file '%s'\n", fileName.c_str());
return false;
}
return WriteModel(stream);
}
bool CModelFile::WriteModel(std::ostream& stream)
{
if (m_triangles.size() == 0)
{
GetLogger()->Error("Empty model\n");
return false;
}
OldModelHeader header;
header.revision = 1;
header.version = 2;
header.totalTriangles = m_triangles.size();
IOUtils::WriteBinary<4, int>(header.revision, stream);
IOUtils::WriteBinary<4, int>(header.version, stream);
IOUtils::WriteBinary<4, int>(header.totalTriangles, stream);
for (int i = 0; i < 10; ++i)
IOUtils::WriteBinary<4, int>(header.reserved[i], stream);
for (int i = 0; i < static_cast<int>( m_triangles.size() ); ++i)
{
OldModelTriangle3 t;
t.used = true;
t.p1 = m_triangles[i].p1;
t.p2 = m_triangles[i].p2;
t.p3 = m_triangles[i].p3;
t.material = m_triangles[i].material;
strncpy(t.texName, m_triangles[i].tex1Name.c_str(), 20);
LODLevelToMinMax(m_triangles[i].lodLevel, t.min, t.max);
t.state = m_triangles[i].state;
int no = 0;
if (m_triangles[i].variableTex2)
no = 1;
else
sscanf(m_triangles[i].tex2Name.c_str(), "dirty%d.png", &no); // hardcoded as in the original code
t.texNum2 = no;
IOUtils::WriteBinary<1, char>(t.used, stream);
IOUtils::WriteBinary<1, char>(t.selected, stream);
/* padding */ IOUtils::WriteBinary<2, unsigned int>(0, stream);
WriteBinaryVertexTex2(t.p1, stream);
WriteBinaryVertexTex2(t.p2, stream);
WriteBinaryVertexTex2(t.p3, stream);
WriteBinaryMaterial(t.material, stream);
stream.write(t.texName, 20);
IOUtils::WriteBinaryFloat(t.min, stream);
IOUtils::WriteBinaryFloat(t.max, stream);
IOUtils::WriteBinary<4, long>(t.state, stream);
IOUtils::WriteBinary<2, short>(t.texNum2, stream);
IOUtils::WriteBinary<2, short>(t.reserved2, stream);
IOUtils::WriteBinary<2, short>(t.reserved3, stream);
IOUtils::WriteBinary<2, short>(t.reserved4, stream);
}
return true;
}
LODLevel CModelFile::MinMaxToLodLevel(float min, float max)
{
if (min == 0.0f && max == 100.0f)
return LOD_High;
else if (min == 100.0f && max == 200.0f)
return LOD_Medium;
else if (min == 200.0f && max == 1000000.0f)
return LOD_Low;
else if (min == 0.0f && max == 1000000.0f)
return LOD_Constant;
return LOD_Constant;
}
void CModelFile::LODLevelToMinMax(LODLevel lodLevel, float& min, float& max)
{
switch (lodLevel)
{
case LOD_High:
min = 0.0f;
max = 100.0f;
break;
case LOD_Medium:
min = 100.0f;
max = 200.0f;
break;
case LOD_Low:
min = 200.0f;
max = 1000000.0f;
break;
case LOD_Constant:
min = 0.0f;
max = 1000000.0f;
break;
}
}
/*******************************************************
New formats
*******************************************************/
/**
* \struct NewModelHeader
* \brief Header for new binary model file
*/
struct NewModelHeader
{
//! File version (1, 2, ...)
int version;
//! Total number of triangles
int totalTriangles;
NewModelHeader()
{
version = 0;
totalTriangles = 0;
}
};
/**
* \struct NewModelTriangle1
* \brief Triangle of new binary model file
*
* NOTE: at this time, it is identical to ModelTriangle struct, but it may change
* independently in the future.
*/
struct NewModelTriangle1
{
//! 1st vertex
VertexTex2 p1;
//! 2nd vertex
VertexTex2 p2;
//! 3rd vertex
VertexTex2 p3;
//! Material
Material material;
//! Name of 1st texture
std::string tex1Name;
//! Name of 2nd texture
std::string tex2Name;
//! If true, 2nd texture will be taken from current engine setting
bool variableTex2;
//! LOD level
int lodLevel;
//! Rendering state to be set
int state;
NewModelTriangle1()
{
variableTex2 = true;
lodLevel = 0;
state = 0;
}
};
bool CModelFile::ReadTextModel(const std::string& fileName)
{
#ifndef MODELFILE_NO_ENGINE
CInputStream stream;
stream.open(fileName);
if (!stream.is_open())
{
GetLogger()->Error("Could not open file '%s'\n", fileName.c_str());
return false;
}
#else
std::ifstream stream;
stream.open(fileName);
if (!stream.good())
{
GetLogger()->Error("Could not open file '%s'\n", fileName.c_str());
return false;
}
#endif
return ReadTextModel(stream);
}
bool CModelFile::ReadTextModel(std::istream& stream)
{
m_triangles.clear();
NewModelHeader header;
bool headOk = ReadLineValue<int>(stream, "version", header.version) &&
ReadLineValue<int>(stream, "total_triangles", header.totalTriangles);
if (!headOk || !stream.good())
{
GetLogger()->Error("Error reading model file header\n");
return false;
}
// New model version 1
if (header.version == 1)
{
for (int i = 0; i < header.totalTriangles; ++i)
{
NewModelTriangle1 t;
std::string p1Text, p2Text, p3Text;
std::string matText;
char varTex2Ch = 0;
bool triOk = ReadLineString(stream, "p1", p1Text) &&
ReadTextVertexTex2(p1Text, t.p1) &&
ReadLineString(stream, "p2", p2Text) &&
ReadTextVertexTex2(p2Text, t.p2) &&
ReadLineString(stream, "p3", p3Text) &&
ReadTextVertexTex2(p3Text, t.p3) &&
ReadLineString(stream, "mat", matText) &&
ReadTextMaterial(matText, t.material) &&
ReadLineValue<std::string>(stream, "tex1", t.tex1Name) &&
ReadLineValue<std::string>(stream, "tex2", t.tex2Name) &&
ReadLineValue<char>(stream, "var_tex2", varTex2Ch) &&
ReadLineValue<int>(stream, "lod_level", t.lodLevel) &&
ReadLineValue<int>(stream, "state", t.state);
if (!triOk || stream.fail())
{
GetLogger()->Error("Error reading model data\n");
return false;
}
t.variableTex2 = varTex2Ch == 'Y';
ModelTriangle triangle;
triangle.p1 = t.p1;
triangle.p2 = t.p2;
triangle.p3 = t.p3;
triangle.material = t.material;
triangle.tex1Name = t.tex1Name;
triangle.tex2Name = t.tex2Name;
triangle.variableTex2 = t.variableTex2;
triangle.state = t.state;
switch (t.lodLevel)
{
case 0: triangle.lodLevel = LOD_Constant; break;
case 1: triangle.lodLevel = LOD_Low; break;
case 2: triangle.lodLevel = LOD_Medium; break;
case 3: triangle.lodLevel = LOD_High; break;
default: break;
}
m_triangles.push_back(triangle);
continue;
}
}
else
{
GetLogger()->Error("Unknown model file version\n");
return false;
}
for (int i = 0; i < static_cast<int>( m_triangles.size() ); ++i)
{
GetLogger()->Trace("ModelTriangle %d\n", i+1);
std::string s1 = m_triangles[i].p1.ToString();
GetLogger()->Trace(" p1: %s\n", s1.c_str());
std::string s2 = m_triangles[i].p2.ToString();
GetLogger()->Trace(" p2: %s\n", s2.c_str());
std::string s3 = m_triangles[i].p3.ToString();
GetLogger()->Trace(" p3: %s\n", s3.c_str());
std::string d = m_triangles[i].material.diffuse.ToString();
std::string a = m_triangles[i].material.ambient.ToString();
std::string s = m_triangles[i].material.specular.ToString();
GetLogger()->Trace(" mat: d: %s a: %s s: %s\n", d.c_str(), a.c_str(), s.c_str());
GetLogger()->Trace(" tex1: %s tex2: %s\n", m_triangles[i].tex1Name.c_str(), m_triangles[i].tex2Name.c_str());
GetLogger()->Trace(" lod level: %d\n", m_triangles[i].lodLevel);
GetLogger()->Trace(" state: %ld\n", m_triangles[i].state);
}
return true;
}
bool CModelFile::WriteTextModel(const std::string &fileName)
{
std::ofstream stream;
stream.open(fileName.c_str(), std::ios_base::out);
if (!stream.good())
{
GetLogger()->Error("Could not open file '%s'\n", fileName.c_str());
return false;
}
return WriteTextModel(stream);
}
bool CModelFile::WriteTextModel(std::ostream& stream)
{
if (m_triangles.size() == 0)
{
GetLogger()->Error("Empty model\n");
return false;
}
NewModelHeader header;
header.version = 1;
header.totalTriangles = m_triangles.size();
stream << "# Colobot text model" << std::endl;
stream << std::endl;
stream << "### HEAD" << std::endl;
stream << "version " << header.version << std::endl;
stream << "total_triangles " << header.totalTriangles << std::endl;
stream << std::endl;
stream << "### TRIANGLES" << std::endl;
for (int i = 0; i < static_cast<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.state = m_triangles[i].state;
switch (m_triangles[i].lodLevel)
{
case LOD_Constant: t.lodLevel = 0; break;
case LOD_Low: t.lodLevel = 1; break;
case LOD_Medium: t.lodLevel = 2; break;
case LOD_High: t.lodLevel = 3; break;
}
stream << "p1 ";
WriteTextVertexTex2(t.p1, stream);
stream << "p2 ";
WriteTextVertexTex2(t.p2, stream);
stream << "p3 ";
WriteTextVertexTex2(t.p3, stream);
stream << "mat ";
WriteTextMaterial(t.material, stream);
stream << "tex1 " << t.tex1Name << std::endl;
stream << "tex2 " << t.tex2Name << std::endl;
stream << "var_tex2 " << (t.variableTex2 ? 'Y' : 'N') << std::endl;
stream << "lod_level " << t.lodLevel << std::endl;
stream << "state " << t.state << std::endl;
stream << std::endl;
if (stream.fail())
{
GetLogger()->Error("Error writing model file\n");
return false;
}
}
return true;
}
bool CModelFile::ReadBinaryModel(const std::string& fileName)
{
#ifndef MODELFILE_NO_ENGINE
CInputStream stream;
stream.open(fileName);
if (!stream.is_open())
{
GetLogger()->Error("Could not open file '%s'\n", fileName.c_str());
return false;
}
#else
std::ifstream stream;
stream.open(fileName);
if (!stream.good())
{
GetLogger()->Error("Could not open file '%s'\n", fileName.c_str());
return false;
}
#endif
return ReadBinaryModel(stream);
}
bool CModelFile::ReadBinaryModel(std::istream& stream)
{
m_triangles.clear();
NewModelHeader header;
header.version = IOUtils::ReadBinary<4, int>(stream);
header.totalTriangles = IOUtils::ReadBinary<4, int>(stream);
if (!stream.good())
{
GetLogger()->Error("Error reading model file header\n");
return false;
}
// New model version 1
if (header.version == 1)
{
for (int i = 0; i < header.totalTriangles; ++i)
{
NewModelTriangle1 t;
ReadBinaryVertexTex2(stream, t.p1);
ReadBinaryVertexTex2(stream, t.p2);
ReadBinaryVertexTex2(stream, t.p3);
ReadBinaryMaterial(stream, t.material);
t.tex1Name = IOUtils::ReadBinaryString<1>(stream);
t.tex2Name = IOUtils::ReadBinaryString<1>(stream);
t.variableTex2 = IOUtils::ReadBinaryBool(stream);
t.lodLevel = IOUtils::ReadBinary<4, int>(stream);
t.state = IOUtils::ReadBinary<4, int>(stream);
if (stream.fail())
{
GetLogger()->Error("Error reading model data\n");
return false;
}
ModelTriangle triangle;
triangle.p1 = t.p1;
triangle.p2 = t.p2;
triangle.p3 = t.p3;
triangle.material = t.material;
triangle.tex1Name = t.tex1Name;
triangle.tex2Name = t.tex2Name;
triangle.variableTex2 = t.variableTex2;
triangle.state = t.state;
switch (t.lodLevel)
{
case 0: triangle.lodLevel = LOD_Constant; break;
case 1: triangle.lodLevel = LOD_Low; break;
case 2: triangle.lodLevel = LOD_Medium; break;
case 3: triangle.lodLevel = LOD_High; break;
default: break;
}
m_triangles.push_back(triangle);
}
}
else
{
GetLogger()->Error("Unknown model file version\n");
return false;
}
if (m_printDebugInfo)
{
for (int i = 0; i < static_cast<int>( m_triangles.size() ); ++i)
{
GetLogger()->Trace("ModelTriangle %d\n", i+1);
std::string s1 = m_triangles[i].p1.ToString();
GetLogger()->Trace(" p1: %s\n", s1.c_str());
std::string s2 = m_triangles[i].p2.ToString();
GetLogger()->Trace(" p2: %s\n", s2.c_str());
std::string s3 = m_triangles[i].p3.ToString();
GetLogger()->Trace(" p3: %s\n", s3.c_str());
std::string d = m_triangles[i].material.diffuse.ToString();
std::string a = m_triangles[i].material.ambient.ToString();
std::string s = m_triangles[i].material.specular.ToString();
GetLogger()->Trace(" mat: d: %s a: %s s: %s\n", d.c_str(), a.c_str(), s.c_str());
GetLogger()->Trace(" tex1: %s tex2: %s\n", m_triangles[i].tex1Name.c_str(), m_triangles[i].tex2Name.c_str());
GetLogger()->Trace(" lod level: %d\n", m_triangles[i].lodLevel);
GetLogger()->Trace(" state: %ld\n", m_triangles[i].state);
}
}
return true;
}
bool CModelFile::WriteBinaryModel(const std::string& fileName)
{
std::ofstream stream;
stream.open(fileName.c_str(), std::ios_base::out | std::ios_base::binary);
if (!stream.good())
{
GetLogger()->Error("Could not open file '%s'\n", fileName.c_str());
return false;
}
return WriteBinaryModel(stream);
}
bool CModelFile::WriteBinaryModel(std::ostream& stream)
{
if (m_triangles.size() == 0)
{
GetLogger()->Error("Empty model\n");
return false;
}
NewModelHeader header;
header.version = 1;
header.totalTriangles = m_triangles.size();
IOUtils::WriteBinary<4, int>(header.version, stream);
IOUtils::WriteBinary<4, int>(header.totalTriangles, stream);
for (int i = 0; i < static_cast<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.state = m_triangles[i].state;
switch (m_triangles[i].lodLevel)
{
case LOD_Constant: t.lodLevel = 0; break;
case LOD_Low: t.lodLevel = 1; break;
case LOD_Medium: t.lodLevel = 2; break;
case LOD_High: t.lodLevel = 3; break;
}
WriteBinaryVertexTex2(t.p1, stream);
WriteBinaryVertexTex2(t.p2, stream);
WriteBinaryVertexTex2(t.p3, stream);
WriteBinaryMaterial(t.material, stream);
IOUtils::WriteBinaryString<1>(t.tex1Name, stream);
IOUtils::WriteBinaryString<1>(t.tex2Name, stream);
IOUtils::WriteBinaryBool(t.variableTex2, stream);
IOUtils::WriteBinary<4, int>(t.lodLevel, stream);
IOUtils::WriteBinary<4, int>(t.state, stream);
if (stream.fail())
{
GetLogger()->Error("Error writing model file\n");
return false;
}
}
return true;
}
const std::vector<ModelTriangle>& CModelFile::GetTriangles()
{
return m_triangles;
}
int CModelFile::GetTriangleCount()
{
return m_triangles.size();
}
void CModelFile::SetPrintDebugInfo(bool printDebugInfo)
{
m_printDebugInfo = printDebugInfo;
}
} // namespace Gfx