Add error checking to 3DS loader
parent
71bfec795c
commit
f3e5444003
|
@ -130,12 +130,12 @@ class M3DScene
|
||||||
M3DScene() = default;
|
M3DScene() = default;
|
||||||
~M3DScene();
|
~M3DScene();
|
||||||
|
|
||||||
M3DModel* getModel(uint32_t) const;
|
M3DModel* getModel(std::uint32_t) const;
|
||||||
uint32_t getModelCount() const;
|
std::uint32_t getModelCount() const;
|
||||||
void addModel(M3DModel*);
|
void addModel(M3DModel*);
|
||||||
|
|
||||||
M3DMaterial* getMaterial(uint32_t) const;
|
M3DMaterial* getMaterial(std::uint32_t) const;
|
||||||
uint32_t getMaterialCount() const;
|
std::uint32_t getMaterialCount() const;
|
||||||
void addMaterial(M3DMaterial*);
|
void addMaterial(M3DMaterial*);
|
||||||
|
|
||||||
M3DColor getBackgroundColor() const;
|
M3DColor getBackgroundColor() const;
|
||||||
|
|
|
@ -7,90 +7,94 @@
|
||||||
// as published by the Free Software Foundation; either version 2
|
// as published by the Free Software Foundation; either version 2
|
||||||
// of the License, or (at your option) any later version.
|
// of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <Eigen/Core>
|
#include <Eigen/Core>
|
||||||
|
#include <fmt/ostream.h>
|
||||||
|
|
||||||
#include "celutil/bytes.h"
|
#include "celutil/bytes.h"
|
||||||
#include "celutil/debug.h"
|
|
||||||
#include "3dschunk.h"
|
#include "3dschunk.h"
|
||||||
#include "3dsmodel.h"
|
#include "3dsmodel.h"
|
||||||
#include "3dsread.h"
|
#include "3dsread.h"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
constexpr std::int32_t READ_FAILURE = -1;
|
||||||
|
constexpr std::int32_t UNKNOWN_CHUNK = -2;
|
||||||
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
using ProcessChunkFunc = bool (*)(std::istream &, std::uint16_t, std::int32_t, T*);
|
using ProcessChunkFunc = std::int32_t (*)(std::istream &, std::uint16_t, std::int32_t, T*);
|
||||||
|
|
||||||
|
|
||||||
std::int32_t readInt(std::istream& in)
|
bool readInt(std::istream& in, std::int32_t& value)
|
||||||
{
|
{
|
||||||
std::int32_t ret;
|
in.read(reinterpret_cast<char*>(&value), sizeof(std::int32_t));
|
||||||
in.read((char *) &ret, sizeof(std::int32_t));
|
if (!in.good()) { return false; }
|
||||||
LE_TO_CPU_INT32(ret, ret);
|
LE_TO_CPU_INT32(value, value);
|
||||||
return ret;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::int16_t readShort(std::istream& in)
|
bool readShort(std::istream& in, std::int16_t& value)
|
||||||
{
|
{
|
||||||
std::int16_t ret;
|
in.read(reinterpret_cast<char*>(&value), sizeof(std::int16_t));
|
||||||
in.read((char *) &ret, sizeof(std::int16_t));
|
if (!in.good()) { return false; }
|
||||||
LE_TO_CPU_INT16(ret, ret);
|
LE_TO_CPU_INT16(value, value);
|
||||||
return ret;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::uint16_t readUshort(std::istream& in)
|
bool readUshort(std::istream& in, std::uint16_t& value)
|
||||||
{
|
{
|
||||||
std::uint16_t ret;
|
in.read(reinterpret_cast<char*>(&value), sizeof(std::uint16_t));
|
||||||
in.read((char *) &ret, sizeof(std::uint16_t));
|
if (!in.good()) { return false; }
|
||||||
LE_TO_CPU_INT16(ret, ret);
|
LE_TO_CPU_INT16(value, value);
|
||||||
return ret;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
float readFloat(std::istream& in)
|
bool readFloat(std::istream& in, float& value)
|
||||||
{
|
{
|
||||||
float f;
|
in.read(reinterpret_cast<char*>(&value), sizeof(float));
|
||||||
in.read((char*) &f, sizeof(float));
|
if (!in.good()) { return false; }
|
||||||
LE_TO_CPU_FLOAT(f, f);
|
LE_TO_CPU_FLOAT(value, value);
|
||||||
return f;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
char readChar(std::istream& in)
|
bool readUchar(std::istream& in, unsigned char& value)
|
||||||
{
|
{
|
||||||
char c;
|
char c;
|
||||||
in.read(&c, 1);
|
in.get(c);
|
||||||
return c;
|
if (!in.good()) { return false; }
|
||||||
|
value = static_cast<unsigned char>(c);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string readString(std::istream& in)
|
std::int32_t readString(std::istream& in, std::string& value)
|
||||||
{
|
{
|
||||||
char s[1024];
|
constexpr std::size_t maxLength = 1024;
|
||||||
int maxLength = sizeof(s);
|
char s[maxLength];
|
||||||
|
|
||||||
for (int count = 0; count < maxLength; count++)
|
for (std::size_t count = 0; count < maxLength; count++)
|
||||||
{
|
{
|
||||||
in.read(s + count, 1);
|
in.read(s + count, 1);
|
||||||
|
if (!in.good()) { return READ_FAILURE; }
|
||||||
if (s[count] == '\0')
|
if (s[count] == '\0')
|
||||||
break;
|
{
|
||||||
|
value = s;
|
||||||
|
return count + 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::string(s);
|
return READ_FAILURE;
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void skipBytes(std::istream& in, int count)
|
|
||||||
{
|
|
||||||
char c;
|
|
||||||
while (count-- > 0)
|
|
||||||
in.get(c);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -99,285 +103,325 @@ std::int32_t read3DSChunk(std::istream& in,
|
||||||
ProcessChunkFunc<T> chunkFunc,
|
ProcessChunkFunc<T> chunkFunc,
|
||||||
T* obj)
|
T* obj)
|
||||||
{
|
{
|
||||||
std::uint16_t chunkType = readUshort(in);
|
std::uint16_t chunkType;
|
||||||
std::int32_t chunkSize = readInt(in);
|
if (!readUshort(in, chunkType)) { return READ_FAILURE; }
|
||||||
|
std::int32_t chunkSize;
|
||||||
|
if (!readInt(in, chunkSize) || chunkSize < 6) { return READ_FAILURE; }
|
||||||
|
|
||||||
std::int32_t contentSize = chunkSize - 6;
|
std::int32_t contentSize = chunkSize - 6;
|
||||||
|
std::int32_t processedSize = chunkFunc(in, chunkType, contentSize, obj);
|
||||||
bool chunkWasRead = chunkFunc(in, chunkType, contentSize, obj);
|
switch (processedSize)
|
||||||
|
|
||||||
if (!chunkWasRead)
|
|
||||||
{
|
{
|
||||||
skipBytes(in, contentSize);
|
case READ_FAILURE:
|
||||||
|
return READ_FAILURE;
|
||||||
|
case UNKNOWN_CHUNK:
|
||||||
|
in.ignore(contentSize);
|
||||||
|
return in.good() ? chunkSize : READ_FAILURE;
|
||||||
|
default:
|
||||||
|
if (processedSize != contentSize)
|
||||||
|
{
|
||||||
|
fmt::print(std::clog, "Chunk type {:04x}, expected {} bytes but read {}\n", chunkType, contentSize, processedSize);
|
||||||
|
return READ_FAILURE;
|
||||||
|
}
|
||||||
|
return chunkSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
return chunkSize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
int read3DSChunks(std::istream& in,
|
std::int32_t read3DSChunks(std::istream& in,
|
||||||
int nBytes,
|
std::int32_t nBytes,
|
||||||
ProcessChunkFunc<T> chunkFunc,
|
ProcessChunkFunc<T> chunkFunc,
|
||||||
T* obj)
|
T* obj)
|
||||||
{
|
{
|
||||||
int bytesRead = 0;
|
std::int32_t bytesRead = 0;
|
||||||
|
|
||||||
while (bytesRead < nBytes)
|
while (bytesRead < nBytes)
|
||||||
bytesRead += read3DSChunk(in, chunkFunc, obj);
|
{
|
||||||
|
std::int32_t chunkSize = read3DSChunk(in, chunkFunc, obj);
|
||||||
|
if (chunkSize < 0) {
|
||||||
|
fmt::print(std::clog, "Failed to read 3DS chunk\n");
|
||||||
|
return READ_FAILURE;
|
||||||
|
}
|
||||||
|
bytesRead += chunkSize;
|
||||||
|
}
|
||||||
|
|
||||||
if (bytesRead != nBytes)
|
if (bytesRead != nBytes)
|
||||||
std::cout << "Expected " << nBytes << " bytes but read " << bytesRead << '\n';
|
{
|
||||||
|
fmt::print(std::clog, "Multiple chunks, expected {} bytes but read {}\n", nBytes, bytesRead);
|
||||||
|
return READ_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
return bytesRead;
|
return bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
M3DColor readColor(std::istream& in/*, int nBytes*/)
|
std::int32_t readColor(std::istream& in, M3DColor& color)
|
||||||
{
|
{
|
||||||
auto r = (unsigned char) readChar(in);
|
unsigned char r, g, b;
|
||||||
auto g = (unsigned char) readChar(in);
|
if (!readUchar(in, r) || !readUchar(in, g) || !readUchar(in, b)) { return READ_FAILURE; }
|
||||||
auto b = (unsigned char) readChar(in);
|
|
||||||
|
|
||||||
return {(float) r / 255.0f,
|
color = {static_cast<float>(r) / 255.0f,
|
||||||
(float) g / 255.0f,
|
static_cast<float>(g) / 255.0f,
|
||||||
(float) b / 255.0f};
|
static_cast<float>(b) / 255.0f};
|
||||||
|
|
||||||
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
M3DColor readFloatColor(std::istream& in/*, int nBytes*/)
|
std::int32_t readFloatColor(std::istream& in, M3DColor& color)
|
||||||
{
|
{
|
||||||
float r = readFloat(in);
|
float r, g, b;
|
||||||
float g = readFloat(in);
|
if (!readFloat(in, r) || !readFloat(in, g) || !readFloat(in, b)) { return READ_FAILURE; }
|
||||||
float b = readFloat(in);
|
|
||||||
|
|
||||||
return {(float) r / 255.0f,
|
color = { r, g, b };
|
||||||
(float) g / 255.0f,
|
return static_cast<std::int32_t>(3 * sizeof(float));
|
||||||
(float) b / 255.0f};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Eigen::Matrix4f readMeshMatrix(std::istream& in/*, int nBytes*/)
|
std::int32_t readMeshMatrix(std::istream& in, Eigen::Matrix4f& m)
|
||||||
{
|
{
|
||||||
float m00 = readFloat(in);
|
float elements[12];
|
||||||
float m01 = readFloat(in);
|
for (std::size_t i = 0; i < 12; ++i)
|
||||||
float m02 = readFloat(in);
|
|
||||||
float m10 = readFloat(in);
|
|
||||||
float m11 = readFloat(in);
|
|
||||||
float m12 = readFloat(in);
|
|
||||||
float m20 = readFloat(in);
|
|
||||||
float m21 = readFloat(in);
|
|
||||||
float m22 = readFloat(in);
|
|
||||||
float m30 = readFloat(in);
|
|
||||||
float m31 = readFloat(in);
|
|
||||||
float m32 = readFloat(in);
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
cout << m00 << " " << m01 << " " << m02 << '\n';
|
|
||||||
cout << m10 << " " << m11 << " " << m12 << '\n';
|
|
||||||
cout << m20 << " " << m21 << " " << m22 << '\n';
|
|
||||||
cout << m30 << " " << m31 << " " << m32 << '\n';
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Eigen::Matrix4f m;
|
|
||||||
m << m00, m01, m02, 0,
|
|
||||||
m10, m11, m12, 0,
|
|
||||||
m20, m21, m22, 0,
|
|
||||||
m30, m31, m32, 1;
|
|
||||||
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void readPointArray(std::istream& in, M3DTriangleMesh* triMesh)
|
|
||||||
{
|
|
||||||
std::uint16_t nPoints = readUshort(in);
|
|
||||||
|
|
||||||
for (int i = 0; i < (int) nPoints; i++)
|
|
||||||
{
|
{
|
||||||
float x = readFloat(in);
|
if (!readFloat(in, elements[i])) { return READ_FAILURE; }
|
||||||
float y = readFloat(in);
|
}
|
||||||
float z = readFloat(in);
|
|
||||||
|
m << elements[0], elements[1], elements[2], 0,
|
||||||
|
elements[3], elements[4], elements[5], 0,
|
||||||
|
elements[6], elements[7], elements[8], 0,
|
||||||
|
elements[9], elements[10], elements[11], 1;
|
||||||
|
|
||||||
|
return static_cast<std::int32_t>(12 * sizeof(float));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::int32_t readPointArray(std::istream& in, M3DTriangleMesh* triMesh)
|
||||||
|
{
|
||||||
|
std::uint16_t nPoints;
|
||||||
|
if (!readUshort(in, nPoints)) { return READ_FAILURE; }
|
||||||
|
std::int32_t bytesRead = static_cast<int>(sizeof(nPoints));
|
||||||
|
|
||||||
|
for (int i = 0; i < static_cast<int>(nPoints); i++)
|
||||||
|
{
|
||||||
|
float x, y, z;
|
||||||
|
if (!readFloat(in, x) || !readFloat(in, y) || !readFloat(in, z)) { return READ_FAILURE; }
|
||||||
|
bytesRead += static_cast<int>(3 * sizeof(float));
|
||||||
triMesh->addVertex(Eigen::Vector3f(x, y, z));
|
triMesh->addVertex(Eigen::Vector3f(x, y, z));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void readTextureCoordArray(std::istream& in, M3DTriangleMesh* triMesh)
|
std::int32_t readTextureCoordArray(std::istream& in, M3DTriangleMesh* triMesh)
|
||||||
{
|
{
|
||||||
std::uint16_t nPoints = readUshort(in);
|
std::int32_t bytesRead = 0;
|
||||||
|
|
||||||
for (int i = 0; i < (int) nPoints; i++)
|
std::uint16_t nPoints;
|
||||||
|
if (!readUshort(in, nPoints)) { return READ_FAILURE; }
|
||||||
|
bytesRead += static_cast<int>(sizeof(nPoints));
|
||||||
|
|
||||||
|
for (int i = 0; i < static_cast<int>(nPoints); i++)
|
||||||
{
|
{
|
||||||
float u = readFloat(in);
|
float u, v;
|
||||||
float v = readFloat(in);
|
if (!readFloat(in, u) || !readFloat(in, v)) { return READ_FAILURE; }
|
||||||
|
bytesRead += static_cast<int>(2 * sizeof(float));
|
||||||
triMesh->addTexCoord(Eigen::Vector2f(u, -v));
|
triMesh->addTexCoord(Eigen::Vector2f(u, -v));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool processFaceArrayChunk(std::istream& in,
|
std::int32_t processFaceArrayChunk(std::istream& in,
|
||||||
std::uint16_t chunkType,
|
std::uint16_t chunkType,
|
||||||
std::int32_t /*contentSize*/,
|
std::int32_t /*contentSize*/,
|
||||||
M3DTriangleMesh* triMesh)
|
M3DTriangleMesh* triMesh)
|
||||||
{
|
{
|
||||||
|
std::int32_t bytesRead = 0;
|
||||||
std::uint16_t nFaces;
|
std::uint16_t nFaces;
|
||||||
M3DMeshMaterialGroup* matGroup;
|
std::unique_ptr<M3DMeshMaterialGroup> matGroup;
|
||||||
|
|
||||||
switch (chunkType)
|
switch (chunkType)
|
||||||
{
|
{
|
||||||
case M3DCHUNK_MESH_MATERIAL_GROUP:
|
case M3DCHUNK_MESH_MATERIAL_GROUP:
|
||||||
matGroup = new M3DMeshMaterialGroup();
|
matGroup = std::make_unique<M3DMeshMaterialGroup>();
|
||||||
|
|
||||||
matGroup->materialName = readString(in);
|
bytesRead = readString(in, matGroup->materialName);
|
||||||
nFaces = readUshort(in);
|
if (bytesRead == READ_FAILURE || !readUshort(in, nFaces)) { return READ_FAILURE; }
|
||||||
|
bytesRead += static_cast<int>(sizeof(nFaces));
|
||||||
|
|
||||||
for (std::uint16_t i = 0; i < nFaces; i++)
|
for (std::uint16_t i = 0; i < nFaces; i++)
|
||||||
{
|
{
|
||||||
std::uint16_t faceIndex = readUshort(in);
|
std::uint16_t faceIndex;
|
||||||
|
if (!readUshort(in, faceIndex)) { return READ_FAILURE; }
|
||||||
|
bytesRead += static_cast<int>(sizeof(faceIndex));
|
||||||
matGroup->faces.push_back(faceIndex);
|
matGroup->faces.push_back(faceIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
triMesh->addMeshMaterialGroup(matGroup);
|
triMesh->addMeshMaterialGroup(matGroup.release());
|
||||||
|
|
||||||
|
return bytesRead;
|
||||||
|
|
||||||
return true;
|
|
||||||
case M3DCHUNK_MESH_SMOOTH_GROUP:
|
case M3DCHUNK_MESH_SMOOTH_GROUP:
|
||||||
nFaces = triMesh->getFaceCount();
|
nFaces = triMesh->getFaceCount();
|
||||||
|
|
||||||
for (std::uint16_t i = 0; i < nFaces; i++)
|
for (std::uint16_t i = 0; i < nFaces; i++)
|
||||||
{
|
{
|
||||||
auto groups = (std::uint32_t) readInt(in);
|
std::int32_t groups;
|
||||||
triMesh->addSmoothingGroups(groups);
|
if (!readInt(in, groups) || groups < 0) { return READ_FAILURE; }
|
||||||
|
bytesRead += static_cast<int>(sizeof(groups));
|
||||||
|
triMesh->addSmoothingGroups(static_cast<std::uint32_t>(groups));
|
||||||
}
|
}
|
||||||
return true;
|
return bytesRead;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return UNKNOWN_CHUNK;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void readFaceArray(std::istream& in, M3DTriangleMesh* triMesh, std::int32_t contentSize)
|
std::int32_t readFaceArray(std::istream& in, M3DTriangleMesh* triMesh, std::int32_t contentSize)
|
||||||
{
|
{
|
||||||
std::uint16_t nFaces = readUshort(in);
|
std::uint16_t nFaces;
|
||||||
|
if (!readUshort(in, nFaces)) { return READ_FAILURE; }
|
||||||
|
std::int32_t bytesRead = static_cast<int>(sizeof(nFaces));
|
||||||
|
|
||||||
for (int i = 0; i < (int) nFaces; i++)
|
for (int i = 0; i < static_cast<int>(nFaces); i++)
|
||||||
{
|
{
|
||||||
std::uint16_t v0 = readUshort(in);
|
std::uint16_t v0, v1, v2, flags;
|
||||||
std::uint16_t v1 = readUshort(in);
|
if (!readUshort(in, v0) || !readUshort(in, v1) || !readUshort(in, v2) || !readUshort(in, flags))
|
||||||
std::uint16_t v2 = readUshort(in);
|
{
|
||||||
/*uint16_t flags = */ readUshort(in);
|
return READ_FAILURE;
|
||||||
|
}
|
||||||
|
bytesRead += static_cast<int>(4 * sizeof(std::uint16_t));
|
||||||
triMesh->addFace(v0, v1, v2);
|
triMesh->addFace(v0, v1, v2);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::int32_t bytesLeft = contentSize - (8 * nFaces + 2);
|
if (bytesRead > contentSize) { return READ_FAILURE; }
|
||||||
if (bytesLeft > 0)
|
|
||||||
|
if (bytesRead < contentSize)
|
||||||
{
|
{
|
||||||
read3DSChunks(in,
|
std::int32_t trailingSize = read3DSChunks(in,
|
||||||
bytesLeft,
|
contentSize - bytesRead,
|
||||||
processFaceArrayChunk,
|
processFaceArrayChunk,
|
||||||
triMesh);
|
triMesh);
|
||||||
|
bytesRead += trailingSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool processTriMeshChunk(std::istream& in,
|
std::int32_t processTriMeshChunk(std::istream& in,
|
||||||
std::uint16_t chunkType,
|
std::uint16_t chunkType,
|
||||||
std::int32_t contentSize,
|
std::int32_t contentSize,
|
||||||
M3DTriangleMesh* triMesh)
|
M3DTriangleMesh* triMesh)
|
||||||
{
|
{
|
||||||
switch (chunkType)
|
switch (chunkType)
|
||||||
{
|
{
|
||||||
case M3DCHUNK_POINT_ARRAY:
|
case M3DCHUNK_POINT_ARRAY:
|
||||||
readPointArray(in, triMesh);
|
return readPointArray(in, triMesh);
|
||||||
return true;
|
|
||||||
case M3DCHUNK_MESH_TEXTURE_COORDS:
|
case M3DCHUNK_MESH_TEXTURE_COORDS:
|
||||||
readTextureCoordArray(in, triMesh);
|
return readTextureCoordArray(in, triMesh);
|
||||||
return true;
|
|
||||||
case M3DCHUNK_FACE_ARRAY:
|
case M3DCHUNK_FACE_ARRAY:
|
||||||
readFaceArray(in, triMesh, contentSize);
|
return readFaceArray(in, triMesh, contentSize);
|
||||||
return true;
|
|
||||||
case M3DCHUNK_MESH_MATRIX:
|
case M3DCHUNK_MESH_MATRIX:
|
||||||
triMesh->setMatrix(readMeshMatrix(in/*, contentSize*/));
|
{
|
||||||
return true;
|
Eigen::Matrix4f matrix;
|
||||||
|
std::int32_t bytesRead = readMeshMatrix(in, matrix);
|
||||||
|
if (bytesRead < 0) { return READ_FAILURE; }
|
||||||
|
triMesh->setMatrix(matrix);
|
||||||
|
return bytesRead;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return UNKNOWN_CHUNK;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool processModelChunk(std::istream& in,
|
std::int32_t processModelChunk(std::istream& in,
|
||||||
std::uint16_t chunkType,
|
std::uint16_t chunkType,
|
||||||
std::int32_t contentSize,
|
std::int32_t contentSize,
|
||||||
M3DModel* model)
|
M3DModel* model)
|
||||||
{
|
{
|
||||||
if (chunkType == M3DCHUNK_TRIANGLE_MESH)
|
if (chunkType == M3DCHUNK_TRIANGLE_MESH)
|
||||||
{
|
{
|
||||||
auto* triMesh = new M3DTriangleMesh();
|
auto triMesh = std::make_unique<M3DTriangleMesh>();
|
||||||
read3DSChunks(in, contentSize, processTriMeshChunk, triMesh);
|
std::int32_t bytesRead = read3DSChunks(in, contentSize, processTriMeshChunk, triMesh.get());
|
||||||
model->addTriMesh(triMesh);
|
if (bytesRead == READ_FAILURE) { return READ_FAILURE; }
|
||||||
return true;
|
model->addTriMesh(triMesh.release());
|
||||||
|
return bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return UNKNOWN_CHUNK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool processColorChunk(std::istream& in,
|
std::int32_t processColorChunk(std::istream& in,
|
||||||
std::uint16_t chunkType,
|
std::uint16_t chunkType,
|
||||||
std::int32_t /*contentSize*/,
|
std::int32_t /*contentSize*/,
|
||||||
M3DColor* color)
|
M3DColor* color)
|
||||||
{
|
{
|
||||||
switch (chunkType)
|
switch (chunkType)
|
||||||
{
|
{
|
||||||
case M3DCHUNK_COLOR_24:
|
case M3DCHUNK_COLOR_24:
|
||||||
*color = readColor(in/*, contentSize*/);
|
return readColor(in, *color);
|
||||||
return true;
|
|
||||||
case M3DCHUNK_COLOR_FLOAT:
|
case M3DCHUNK_COLOR_FLOAT:
|
||||||
*color = readFloatColor(in/*, contentSize*/);
|
return readFloatColor(in, *color);
|
||||||
return true;
|
default:
|
||||||
|
return UNKNOWN_CHUNK;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool processPercentageChunk(std::istream& in,
|
std::int32_t processPercentageChunk(std::istream& in,
|
||||||
std::uint16_t chunkType,
|
std::uint16_t chunkType,
|
||||||
std::int32_t /*contentSize*/,
|
std::int32_t /*contentSize*/,
|
||||||
float* percent)
|
float* percent)
|
||||||
{
|
{
|
||||||
switch (chunkType)
|
switch (chunkType)
|
||||||
{
|
{
|
||||||
case M3DCHUNK_INT_PERCENTAGE:
|
case M3DCHUNK_INT_PERCENTAGE:
|
||||||
*percent = readShort(in);
|
{
|
||||||
return true;
|
std::int16_t value;
|
||||||
|
if (!readShort(in, value)) { return READ_FAILURE; }
|
||||||
|
*percent = static_cast<float>(value);
|
||||||
|
return sizeof(value);
|
||||||
|
}
|
||||||
case M3DCHUNK_FLOAT_PERCENTAGE:
|
case M3DCHUNK_FLOAT_PERCENTAGE:
|
||||||
*percent = readFloat(in);
|
return readFloat(in, *percent) ? sizeof(float) : READ_FAILURE;
|
||||||
return true;
|
default:
|
||||||
|
return UNKNOWN_CHUNK;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool processTexmapChunk(std::istream& in,
|
std::int32_t processTexmapChunk(std::istream& in,
|
||||||
std::uint16_t chunkType,
|
std::uint16_t chunkType,
|
||||||
std::int32_t /*contentSize*/,
|
std::int32_t /*contentSize*/,
|
||||||
M3DMaterial* material)
|
M3DMaterial* material)
|
||||||
{
|
{
|
||||||
if (chunkType == M3DCHUNK_MATERIAL_MAPNAME)
|
if (chunkType == M3DCHUNK_MATERIAL_MAPNAME)
|
||||||
{
|
{
|
||||||
std::string name = readString(in);
|
std::string name;
|
||||||
|
std::int32_t bytesRead = readString(in, name);
|
||||||
|
if (bytesRead < 0) { return READ_FAILURE; }
|
||||||
material->setTextureMap(name);
|
material->setTextureMap(name);
|
||||||
return true;
|
return bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return UNKNOWN_CHUNK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool processMaterialChunk(std::istream& in,
|
std::int32_t processMaterialChunk(std::istream& in,
|
||||||
std::uint16_t chunkType,
|
std::uint16_t chunkType,
|
||||||
std::int32_t contentSize,
|
std::int32_t contentSize,
|
||||||
M3DMaterial* material)
|
M3DMaterial* material)
|
||||||
{
|
{
|
||||||
|
std::int32_t bytesRead;
|
||||||
std::string name;
|
std::string name;
|
||||||
M3DColor color;
|
M3DColor color;
|
||||||
float t;
|
float t;
|
||||||
|
@ -385,92 +429,101 @@ bool processMaterialChunk(std::istream& in,
|
||||||
switch (chunkType)
|
switch (chunkType)
|
||||||
{
|
{
|
||||||
case M3DCHUNK_MATERIAL_NAME:
|
case M3DCHUNK_MATERIAL_NAME:
|
||||||
name = readString(in);
|
bytesRead = readString(in, name);
|
||||||
|
if (bytesRead < 0) { return READ_FAILURE; }
|
||||||
material->setName(name);
|
material->setName(name);
|
||||||
return true;
|
return bytesRead;
|
||||||
case M3DCHUNK_MATERIAL_AMBIENT:
|
case M3DCHUNK_MATERIAL_AMBIENT:
|
||||||
read3DSChunks(in, contentSize, processColorChunk, &color);
|
bytesRead = read3DSChunks(in, contentSize, processColorChunk, &color);
|
||||||
|
if (bytesRead < 0) { return READ_FAILURE; }
|
||||||
material->setAmbientColor(color);
|
material->setAmbientColor(color);
|
||||||
return true;
|
return bytesRead;
|
||||||
case M3DCHUNK_MATERIAL_DIFFUSE:
|
case M3DCHUNK_MATERIAL_DIFFUSE:
|
||||||
read3DSChunks(in, contentSize, processColorChunk, &color);
|
bytesRead = read3DSChunks(in, contentSize, processColorChunk, &color);
|
||||||
|
if (bytesRead < 0) { return READ_FAILURE; }
|
||||||
material->setDiffuseColor(color);
|
material->setDiffuseColor(color);
|
||||||
return true;
|
return bytesRead;
|
||||||
case M3DCHUNK_MATERIAL_SPECULAR:
|
case M3DCHUNK_MATERIAL_SPECULAR:
|
||||||
read3DSChunks(in, contentSize, processColorChunk, &color);
|
bytesRead = read3DSChunks(in, contentSize, processColorChunk, &color);
|
||||||
|
if (bytesRead < 0) { return READ_FAILURE; }
|
||||||
material->setSpecularColor(color);
|
material->setSpecularColor(color);
|
||||||
return true;
|
return bytesRead;
|
||||||
case M3DCHUNK_MATERIAL_SHININESS:
|
case M3DCHUNK_MATERIAL_SHININESS:
|
||||||
read3DSChunks(in, contentSize, processPercentageChunk, &t);
|
bytesRead = read3DSChunks(in, contentSize, processPercentageChunk, &t);
|
||||||
|
if (bytesRead < 0) { return READ_FAILURE; }
|
||||||
material->setShininess(t);
|
material->setShininess(t);
|
||||||
return true;
|
return bytesRead;
|
||||||
case M3DCHUNK_MATERIAL_TRANSPARENCY:
|
case M3DCHUNK_MATERIAL_TRANSPARENCY:
|
||||||
read3DSChunks(in, contentSize, processPercentageChunk, &t);
|
bytesRead = read3DSChunks(in, contentSize, processPercentageChunk, &t);
|
||||||
|
if (bytesRead < 0) { return READ_FAILURE; }
|
||||||
material->setOpacity(1.0f - t / 100.0f);
|
material->setOpacity(1.0f - t / 100.0f);
|
||||||
return true;
|
return bytesRead;
|
||||||
case M3DCHUNK_MATERIAL_TEXMAP:
|
case M3DCHUNK_MATERIAL_TEXMAP:
|
||||||
read3DSChunks(in, contentSize, processTexmapChunk, material);
|
return read3DSChunks(in, contentSize, processTexmapChunk, material);
|
||||||
return true;
|
default:
|
||||||
|
return UNKNOWN_CHUNK;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool processSceneChunk(std::istream& in,
|
std::int32_t processSceneChunk(std::istream& in,
|
||||||
std::uint16_t chunkType,
|
std::uint16_t chunkType,
|
||||||
std::int32_t contentSize,
|
std::int32_t contentSize,
|
||||||
M3DScene* scene)
|
M3DScene* scene)
|
||||||
{
|
{
|
||||||
M3DModel* model;
|
std::int32_t bytesRead, chunksSize;
|
||||||
M3DMaterial* material;
|
std::unique_ptr<M3DModel> model;
|
||||||
|
std::unique_ptr<M3DMaterial> material;
|
||||||
M3DColor color;
|
M3DColor color;
|
||||||
std::string name;
|
std::string name;
|
||||||
|
|
||||||
switch (chunkType)
|
switch (chunkType)
|
||||||
{
|
{
|
||||||
case M3DCHUNK_NAMED_OBJECT:
|
case M3DCHUNK_NAMED_OBJECT:
|
||||||
name = readString(in);
|
bytesRead = readString(in, name);
|
||||||
model = new M3DModel();
|
if (bytesRead < 0) { return READ_FAILURE; }
|
||||||
|
model = std::make_unique<M3DModel>();
|
||||||
model->setName(name);
|
model->setName(name);
|
||||||
read3DSChunks(in,
|
chunksSize = read3DSChunks(in,
|
||||||
contentSize - (name.length() + 1),
|
contentSize - bytesRead,
|
||||||
processModelChunk,
|
processModelChunk,
|
||||||
model);
|
model.get());
|
||||||
scene->addModel(model);
|
if (chunksSize < 0) { return READ_FAILURE; }
|
||||||
|
scene->addModel(model.release());
|
||||||
|
|
||||||
return true;
|
return bytesRead + chunksSize;
|
||||||
case M3DCHUNK_MATERIAL_ENTRY:
|
case M3DCHUNK_MATERIAL_ENTRY:
|
||||||
material = new M3DMaterial();
|
material = std::make_unique<M3DMaterial>();
|
||||||
read3DSChunks(in,
|
bytesRead = read3DSChunks(in,
|
||||||
contentSize,
|
contentSize,
|
||||||
processMaterialChunk,
|
processMaterialChunk,
|
||||||
material);
|
material.get());
|
||||||
scene->addMaterial(material);
|
if (bytesRead < 0) { return READ_FAILURE; }
|
||||||
|
scene->addMaterial(material.release());
|
||||||
|
|
||||||
return true;
|
return bytesRead;
|
||||||
case M3DCHUNK_BACKGROUND_COLOR:
|
case M3DCHUNK_BACKGROUND_COLOR:
|
||||||
read3DSChunks(in, contentSize, processColorChunk, &color);
|
bytesRead = read3DSChunks(in, contentSize, processColorChunk, &color);
|
||||||
|
if (bytesRead < 0) { return READ_FAILURE; }
|
||||||
scene->setBackgroundColor(color);
|
scene->setBackgroundColor(color);
|
||||||
return true;
|
return bytesRead;
|
||||||
default:
|
default:
|
||||||
return false;
|
return UNKNOWN_CHUNK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool processTopLevelChunk(std::istream& in,
|
std::int32_t processTopLevelChunk(std::istream& in,
|
||||||
std::uint16_t chunkType,
|
std::uint16_t chunkType,
|
||||||
std::int32_t contentSize,
|
std::int32_t contentSize,
|
||||||
M3DScene* scene)
|
M3DScene* scene)
|
||||||
{
|
{
|
||||||
if (chunkType == M3DCHUNK_MESHDATA)
|
if (chunkType == M3DCHUNK_MESHDATA)
|
||||||
{
|
{
|
||||||
read3DSChunks(in, contentSize, processSceneChunk, scene);
|
return read3DSChunks(in, contentSize, processSceneChunk, scene);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return UNKNOWN_CHUNK;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end namespace
|
} // end namespace
|
||||||
|
@ -478,28 +531,33 @@ bool processTopLevelChunk(std::istream& in,
|
||||||
|
|
||||||
M3DScene* Read3DSFile(std::istream& in)
|
M3DScene* Read3DSFile(std::istream& in)
|
||||||
{
|
{
|
||||||
std::uint16_t chunkType = readUshort(in);
|
std::uint16_t chunkType;
|
||||||
if (chunkType != M3DCHUNK_MAGIC)
|
if (!readUshort(in, chunkType) || chunkType != M3DCHUNK_MAGIC)
|
||||||
{
|
{
|
||||||
DPRINTF(LOG_LEVEL_ERROR, "Read3DSFile: Wrong magic number in header\n");
|
fmt::print(std::clog, "Read3DSFile: Wrong magic number in header\n");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::int32_t chunkSize = readInt(in);
|
std::int32_t chunkSize;
|
||||||
if (in.bad())
|
if (!readInt(in, chunkSize) || chunkSize < 6)
|
||||||
{
|
{
|
||||||
DPRINTF(LOG_LEVEL_ERROR, "Read3DSFile: Error reading 3DS file.\n");
|
fmt::print(std::clog, "Read3DSFile: Error reading 3DS file top level chunk size\n");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
DPRINTF(LOG_LEVEL_INFO, "3DS file, %d bytes\n", chunkSize);
|
fmt::print(std::clog, "3DS file, {} bytes\n", chunkSize + 6);
|
||||||
|
|
||||||
auto* scene = new M3DScene();
|
auto scene = std::make_unique<M3DScene>();
|
||||||
std::int32_t contentSize = chunkSize - 6;
|
std::int32_t contentSize = chunkSize - 6;
|
||||||
|
|
||||||
read3DSChunks(in, contentSize, processTopLevelChunk, scene);
|
std::int32_t bytesRead = read3DSChunks(in, contentSize, processTopLevelChunk, scene.get());
|
||||||
|
if (bytesRead < 0) { return nullptr; }
|
||||||
|
if (bytesRead != contentSize)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
return scene;
|
return scene.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -508,7 +566,7 @@ M3DScene* Read3DSFile(const fs::path& filename)
|
||||||
std::ifstream in(filename.string(), std::ios::in | std::ios::binary);
|
std::ifstream in(filename.string(), std::ios::in | std::ios::binary);
|
||||||
if (!in.good())
|
if (!in.good())
|
||||||
{
|
{
|
||||||
DPRINTF(LOG_LEVEL_ERROR, "Read3DSFile: Error opening %s\n", filename);
|
fmt::print(std::clog, "Read3DSFile: Error opening {}\n", filename);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue