New model file format

- added new binary and text formats for models
- fixes and improvements in CModelFile
- tool for converting model files
- minor additions and fixes
dev-ui
Piotr Dziwinski 2012-08-31 20:55:16 +02:00
parent 3e52ae4ca9
commit e94e26ae1e
11 changed files with 1195 additions and 366 deletions

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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:

File diff suppressed because it is too large Load Diff

View File

@ -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;
int state;
ModelTriangle();
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;
};

View File

@ -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)
{

View File

@ -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

13
src/tools/CMakeLists.txt Normal file
View File

@ -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})

4
src/tools/README.txt Normal file
View File

@ -0,0 +1,4 @@
/**
* \dir tools
* \brief Various tools (separate programs)
*/

285
src/tools/convert_model.cpp Normal file
View File

@ -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;
}