1250 lines
36 KiB
C++
1250 lines
36 KiB
C++
/*
|
||
* 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
|
||
|