#include <iostream>
#include <dae.h>
#include <dom/domCOLLADA.h>
#include <ctime>
#include <sstream>
#include <iostream>
#include <string>
#include <fstream>
#include <cmath>
typedef unsigned char uchar;
typedef unsigned int uint;
class Vec3 {
public:
Vec3() {
this->x = 0;
this->y = 0;
this->z = 0;
}
Vec3(float x, float y, float z) {
this->x = x;
this->y = y;
this->z = z;
}
float X() {
return x;
}
float Y() {
return y;
}
float Z() {
return z;
}
protected:
float x;
float y;
float z;
};
class Vec2 {
public:
Vec2() {
this->u = 0;
this->v = 0;
}
Vec2(float u, float v) {
this->u = u;
this->v = v;
}
float U() {
return u;
}
float V() {
return v;
}
protected:
float u;
float v;
};
class RGBA {
public:
RGBA(uchar r, uchar g, uchar b, uchar a) {
this->r = r;
this->g = g;
this->b = b;
this->a = a;
}
uchar R() {
return r;
}
uchar G() {
return g;
}
uchar B() {
return b;
}
uchar A() {
return a;
}
protected:
uchar r;
uchar g;
uchar b;
uchar a;
};
class Vec3Group: public std::vector<Vec3> {
public:
Vec3Group() {
set = false;
}
void SetBound(Vec3 min, Vec3 max) {
if (!set) {
this->min = min;
this->max = max;
set = true;
}
}
Vec3 BoundingMin() {
return min;
}
Vec3 BoundingMax() {
return max;
}
protected:
bool set;
Vec3 min;
Vec3 max;
};
class Vec2Group: public std::vector<Vec2> {
public:
Vec2Group() {
set = false;
}
void SetBound(Vec2 min, Vec2 max) {
if (!set) {
this->min = min;
this->max = max;
set = true;
}
}
Vec2 BoundingMin() {
return min;
}
Vec2 BoundingMax() {
return max;
}
protected:
bool set;
Vec2 min;
Vec2 max;
};
typedef std::vector<RGBA> RGBAGroup;
class Vertex {
public:
Vertex() {
vertex = 0;
normal = 0;
color = 0;
//uv = NULL;
uvCount = 0;
}
~Vertex() {
/*if (uv != NULL) {
delete[] uv;
uv = NULL;
}*/
}
void Set(uint16_t vertex, uint16_t normal, uint16_t color, uint16_t uv[],
int numUV) {
this->vertex = vertex;
this->normal = normal;
this->color = color;
//this->uv = new uint16_t[numUV];
for (int i = 0; i < numUV; i++) {
this->uv[i] = uv[i];
}
this->uvCount = numUV;
}
uint16_t GetVertex() {
return vertex;
}
uint16_t GetNormal() {
return normal;
}
uint16_t GetColor() {
return color;
}
uint16_t GetUV(int id) {
if (id >= 0 && id < uvCount) {
return uv[id];
}
else
return 0;
}
int UVCount() {
return uvCount;
}
private:
uint16_t vertex;
uint16_t normal;
uint16_t color;
uint16_t uv[8];
int uvCount;
};
class TriangleStrip {
public:
TriangleStrip() {
locked = false;
}
~TriangleStrip() {
}
Vertex& operator[](size_t index) {
return verts[index];
}
void Add(Vertex vert) {
if (!locked) {
verts.push_back(vert);
}
}
void Lock() {
locked = true;
}
size_t Count() {
return verts.size();
}
private:
bool locked;
std::vector<Vertex> verts;
};
class TriangleFan {
public:
TriangleFan(Vertex origin) {
this->origin = origin;
locked = false;
}
~TriangleFan() {
}
Vertex& operator[](size_t index) {
return verts[index];
}
void Add(Vertex vert) {
if (!locked) {
verts.push_back(vert);
}
}
void Lock() {
locked = true;
}
size_t Count() {
return verts.size();
}
Vertex GetOriginVertex() {
return origin;
}
private:
bool locked;
Vertex origin;
std::vector<Vertex> verts;
};
class Triangle {
public:
Triangle(Vertex p1, Vertex p2, Vertex p3) {
this->p1 = p1;
this->p2 = p2;
this->p3 = p3;
}
~Triangle() {
}
Vertex& operator[](size_t index) {
switch (index) {
case 0:
return p1;
case 1:
return p2;
case 2:
return p3;
default:
return p3;
}
}
private:
Vertex p1;
Vertex p2;
Vertex p3;
};
class Quad {
public:
Quad(Vertex p1, Vertex p2, Vertex p3, Vertex p4) {
this->p1 = p1;
this->p2 = p2;
this->p3 = p3;
this->p4 = p4;
}
~Quad() {
}
Vertex& operator[](size_t index) {
switch (index) {
case 0:
return p1;
case 1:
return p2;
case 2:
return p3;
case 3:
return p4;
default:
return p4;
}
}
private:
Vertex p1;
Vertex p2;
Vertex p3;
Vertex p4;
};
class PolyGroup {
public:
PolyGroup() {
locked = false;
vertex = 0;
vertexPtr = NULL;
normal = 0;
normalPtr = NULL;
color = 0;
colorPtr = NULL;
}
~PolyGroup() {
}
void SetVertex(uint16_t vertex, Vec3Group* vertexPtr) {
if (!locked) {
this->vertex = vertex;
this->vertexPtr = vertexPtr;
}
}
void SetNormal(uint16_t normal, Vec3Group* normalPtr) {
if (!locked) {
this->normal = normal;
this->normalPtr = normalPtr;
}
}
void SetColor(uint16_t color, RGBAGroup* colorPtr) {
if (!locked) {
this->color = color;
this->colorPtr = colorPtr;
}
}
void AddUV(uint16_t uvid, Vec2Group* uvPtr) {
if (!locked) {
this->uvIDs.push_back(uvid);
this->uvPtrs.push_back(uvPtr);
}
}
void Lock() {
locked = true;
}
void AddTriangleStrip(TriangleStrip tristrip) {
if (!locked) {
triangleStrips.push_back(tristrip);
}
}
void AddTriangleFan(TriangleFan trifan) {
if (!locked) {
triangleFans.push_back(trifan);
}
}
void AddTriangle(Triangle triangle) {
if (!locked) {
triangles.push_back(triangle);
}
}
void AddQuad(Quad quad) {
if (!locked) {
quads.push_back(quad);
}
}
size_t TriangleStripCount() {
return triangleStrips.size();
}
size_t TriangleFanCount() {
return triangleFans.size();
}
size_t TriangleCount() {
return triangles.size();
}
size_t QuadCount() {
return quads.size();
}
TriangleStrip GetTriangleStrip(size_t index) {
return triangleStrips[index];
}
TriangleFan GetTriangleFan(size_t index) {
return triangleFans[index];
}
Triangle GetTriangle(size_t index) {
return triangles[index];
}
Quad GetQuad(size_t index) {
return quads[index];
}
uint16_t VertexGroup() {
return vertex;
}
Vec3Group* VertexGroupPtr() {
return vertexPtr;
}
uint16_t NormalGroup() {
return normal;
}
Vec3Group* NormalGroupPtr() {
return normalPtr;
}
uint16_t ColorGroup() {
return color;
}
RGBAGroup* ColorGroupPtr() {
return colorPtr;
}
uint16_t UVGroup(uint id) {
if (id < uvIDs.size())
return uvIDs[id];
}
Vec2Group* UVGroupPtr(uint id) {
if (id < uvPtrs.size())
return uvPtrs[id];
else
return NULL;
}
int UVGroupCount() {
return uvIDs.size();
}
private:
bool locked;
uint16_t vertex;
Vec3Group* vertexPtr;
uint16_t normal;
Vec3Group* normalPtr;
uint16_t color;
RGBAGroup* colorPtr;
std::vector<uint16_t> uvIDs;
std::vector<Vec2Group*> uvPtrs;
std::vector<TriangleStrip> triangleStrips;
std::vector<TriangleFan> triangleFans;
std::vector<Triangle> triangles;
std::vector<Quad> quads;
};
extern float BigFloat(const char* data, unsigned int offset=0);
extern uint32_t BigUInt32(const char* data, unsigned int offset=0);
extern uint16_t BigUInt16(const char* data, unsigned int offset=0);
extern int16_t BigSInt16(const char* data, unsigned int offset=0);
float BigFloat(const char* data, unsigned int offset) {
union {
float f;
unsigned char b[4];
} dat;
dat.b[0] = data[offset + 3];
dat.b[1] = data[offset + 2];
dat.b[2] = data[offset + 1];
dat.b[3] = data[offset];
return dat.f;
}
uint32_t BigUInt32(const char* data, unsigned int offset) {
union {
uint32_t u;
unsigned char b[4];
} dat;
dat.b[0] = data[offset + 3];
dat.b[1] = data[offset + 2];
dat.b[2] = data[offset + 1];
dat.b[3] = data[offset];
return dat.u;
}
uint16_t BigUInt16(const char* data, unsigned int offset) {
union {
uint16_t u;
unsigned char b[2];
} dat;
dat.b[0] = data[offset + 1];
dat.b[1] = data[offset];
return dat.u;
}
int16_t BigSInt16(const char* data, unsigned int offset) {
union {
int16_t u;
unsigned char b[2];
} dat;
dat.b[0] = data[offset + 1];
dat.b[1] = data[offset];
return dat.u;
}
typedef enum {
LO_CONV_TO_TRIS = 1 // converts all triangle strips/fans and quads to triangles
} LoadOptions;
class MDL0File {
public:
MDL0File();
virtual ~MDL0File();
/**
* Load an MDL0 file.
*
* @param data An array containing the all the data in the file.
* @return True on a success, false on failure.
*/
bool Load(const char* data, uint length, uint options);
void Unload();
PolyGroup* GetPolygonGroup(int id);
int PolygonGroupCount();
Vec3Group* GetVertexGroup(int id);
int VertexGroupCount();
RGBAGroup* GetColorGroup(int id);
int ColorGroupCount();
Vec3Group* GetNormalGroup(int id);
int NormalGroupCount();
Vec2Group* GetUVGroup(int id);
int UVGroupCount();
private:
bool loadGroup(const char* data, int type);
bool loadVec3Group(const char* data, Vec3Group* group);
bool loadVec2Group(const char* data, Vec2Group* group);
bool loadRGBAGroup(const char* data, RGBAGroup* group);
bool loadPolyGroup(const char* data, PolyGroup* group);
Vertex loadVertex(const char* data, int offset, std::vector<int> fmt,
bool hasNormal, bool hasColor, int unknown, int uv, int& outLength);
bool loaded;
uint options;
PolyGroup* polyGroups;
int polyGroupCount;
Vec3Group* vertexGroups;
int vertexGroupCount;
RGBAGroup* colorGroups;
int colorGroupCount;
Vec3Group* normalGroups;
int normalGroupCount;
Vec2Group* uvGroups;
int uvGroupCount;
};
MDL0File::MDL0File() {
loaded = false;
polyGroups = NULL;
polyGroupCount = 0;
vertexGroups = NULL;
vertexGroupCount = 0;
colorGroups = NULL;
colorGroupCount = 0;
normalGroups = NULL;
normalGroupCount = 0;
uvGroups = NULL;
uvGroupCount = 0;
options = 0;
}
MDL0File::~MDL0File() {
if (vertexGroups != NULL) {
delete[] vertexGroups;
vertexGroups = NULL;
}
if (normalGroups != NULL) {
delete[] normalGroups;
normalGroups = NULL;
}
if (colorGroups != NULL) {
delete[] colorGroups;
colorGroups = NULL;
}
if (uvGroups != NULL) {
delete[] uvGroups;
uvGroups = NULL;
}
if (polyGroups != NULL) {
delete[] polyGroups;
polyGroups = NULL;
}
}
bool MDL0File::Load(const char* data, uint length, uint convToTris) {
if (data == NULL) {
return false;
}
// we need the array to be at least as long as the header
if (length < 64)
return false;
// check for "MDL0"
if (data[0] != 'M' || data[1] != 'D' || data[2] != 'L' || data[3] != '0')
return false;
this->options = options;
const char *groupPtrs[11];
for (int i = 0; i < 11; i++) {
uint32_t ptr = BigUInt32(data, 16 + i * 4);
if (ptr >= length)
return false; // pointer is out of range
if (ptr == 0)
groupPtrs[i] = NULL;
else
groupPtrs[i] = data + ptr;
}
for (int i = 0; i < 11; i++) {
if (groupPtrs[i] != NULL) {
loadGroup(groupPtrs[i], i);
}
}
loaded = true;
return true;
}
void MDL0File::Unload() {
}
PolyGroup* MDL0File::GetPolygonGroup(int id) {
if (id < polyGroupCount)
return &polyGroups[id];
else
return NULL;
}
int MDL0File::PolygonGroupCount() {
return polyGroupCount;
}
Vec3Group* MDL0File::GetVertexGroup(int id) {
if (id < vertexGroupCount)
return &vertexGroups[id];
else
return NULL;
}
int MDL0File::VertexGroupCount() {
return vertexGroupCount;
}
RGBAGroup* MDL0File::GetColorGroup(int id) {
if (id < colorGroupCount)
return &colorGroups[id];
else
return NULL;
}
int MDL0File::ColorGroupCount() {
return colorGroupCount;
}
Vec3Group* MDL0File::GetNormalGroup(int id) {
if (id < normalGroupCount)
return &normalGroups[id];
else
return NULL;
}
int MDL0File::NormalGroupCount() {
return normalGroupCount;
}
Vec2Group* MDL0File::GetUVGroup(int id) {
if (id < uvGroupCount)
return &uvGroups[id];
else
return NULL;
}
int MDL0File::UVGroupCount() {
return uvGroupCount;
}
bool MDL0File::loadGroup(const char* data, int type) {
uint32_t itemcount = BigUInt32(data, 4);
switch (type) {
case 0: // unknown
break;
case 1: // probably bones, don't know how to load yet
break;
case 2: // vertices
vertexGroups = new Vec3Group[itemcount];
vertexGroupCount = itemcount;
break;
case 3: // normals
normalGroups = new Vec3Group[itemcount];
normalGroupCount = itemcount;
break;
case 4: // colors
colorGroups = new RGBAGroup[itemcount];
colorGroupCount = itemcount;
break;
case 5: // uv coordinates
uvGroups = new Vec2Group[itemcount];
uvGroupCount = itemcount;
break;
case 6: // unknown
break;
case 7: // unknown
break;
case 8: // polygon groups
polyGroups = new PolyGroup[itemcount];
polyGroupCount = itemcount;
break;
case 9: // unknown
break;
case 10: // unknown
break;
}
for (uint i = 0; i < itemcount; i++) {
uint32_t dataoffset = BigUInt32(data, 36 + 16 * i);
switch (type) {
case 0: // unknown
break;
case 1: // probably bones, don't know how to load yet
break;
case 2: // vertices
if (!loadVec3Group(data + dataoffset, &vertexGroups[i]))
return false;
break;
case 3: // normals
if (!loadVec3Group(data + dataoffset, &normalGroups[i]))
return false;
break;
case 4: // colors
if (!loadRGBAGroup(data + dataoffset, &colorGroups[i]))
return false;
break;
case 5: // uv coordinates
if (!loadVec2Group(data + dataoffset, &uvGroups[i]))
return false;
break;
case 6: // unknown
break;
case 7: // unknown
break;
case 8: // polygon groups
if (!loadPolyGroup(data + dataoffset, &polyGroups[i]))
return false;
break;
case 9: // unknown
break;
case 10: // unknown
break;
}
}
return true;
}
bool MDL0File::loadVec3Group(const char* data, Vec3Group* group) {
bool boundingbox = data[23];
char datatype = data[27];
char fixedpointbits = data[28];
uint16_t datacount = BigUInt16(data, 30);
int datastart = 32;
if (boundingbox) {
float minx = BigFloat(data, 32);
float miny = BigFloat(data, 36);
float minz = BigFloat(data, 40);
float maxx = BigFloat(data, 44);
float maxy = BigFloat(data, 48);
float maxz = BigFloat(data, 52);
group->SetBound(Vec3(minx, miny, minz), Vec3(maxx, maxy, maxz));
datastart = 64;
} else {
group->SetBound(Vec3(0, 0, 0), Vec3(0, 0, 0));
}
switch (datatype) {
case 0: // fixed point u8
for (int i = 0; i < datacount; i++) {
float x = (unsigned char) data[datastart + i * 6] / pow(2.0f,
fixedpointbits);
float y = (unsigned char) data[datastart + 1 + i * 6] / pow(2.0f,
fixedpointbits);
float z = (unsigned char) data[datastart + 2 + i * 6] / pow(2.0f,
fixedpointbits);
group->push_back(Vec3(x, y, z));
}
break;
case 1: // fixed point s8
for (int i = 0; i < datacount; i++) {
float x = data[datastart + i * 6] / pow(2.0f, fixedpointbits);
float y = data[datastart + 1 + i * 6] / pow(2.0f, fixedpointbits);
float z = data[datastart + 2 + i * 6] / pow(2.0f, fixedpointbits);
group->push_back(Vec3(x, y, z));
}
break;
case 2: // fixed point u16
for (int i = 0; i < datacount; i++) {
float x = BigUInt16(data, datastart + i * 6) / pow(2.0f,
fixedpointbits);
float y = BigUInt16(data, datastart + 2 + i * 6) / pow(2.0f,
fixedpointbits);
float z = BigUInt16(data, datastart + 4 + i * 6) / pow(2.0f,
fixedpointbits);
group->push_back(Vec3(x, y, z));
}
break;
case 3: // fixed point s16
for (int i = 0; i < datacount; i++) {
float x = BigSInt16(data, datastart + i * 6) / pow(2.0f,
fixedpointbits);
float y = BigSInt16(data, datastart + 2 + i * 6) / pow(2.0f,
fixedpointbits);
float z = BigSInt16(data, datastart + 4 + i * 6) / pow(2.0f,
fixedpointbits);
group->push_back(Vec3(x, y, z));
}
break;
case 4: // floating point
for (int i = 0; i < datacount; i++) {
float x = BigFloat(data, datastart + i * 12);
float y = BigFloat(data, datastart + 4 + i * 12);
float z = BigFloat(data, datastart + 8 + i * 12);
group->push_back(Vec3(x, y, z));
}
break;
default:
return false;
}
return true;
}
bool MDL0File::loadVec2Group(const char* data, Vec2Group* group) {
bool boundingbox = data[23];
char datatype = data[27];
char fixedpointbits = data[28];
uint16_t datacount = BigUInt16(data, 30);
int datastart = 32;
if (boundingbox) {
float minu = BigFloat(data, 32);
float minv = BigFloat(data, 36);
float maxu = BigFloat(data, 40);
float maxv = BigFloat(data, 44);
group->SetBound(Vec2(minu, minv), Vec2(maxu, maxv));
datastart = 64;
} else {
group->SetBound(Vec2(0, 0), Vec2(0, 0));
}
switch (datatype) {
case 0: // fixed point u8
for (int i = 0; i < datacount; i++) {
float u = (unsigned char) data[datastart + i * 6] / pow(2.0f,
fixedpointbits);
float v = (unsigned char) data[datastart + 1 + i * 6] / pow(2.0f,
fixedpointbits);
group->push_back(Vec2(u, v));
}
break;
case 1: // fixed point s8
for (int i = 0; i < datacount; i++) {
float u = data[datastart + i * 6] / pow(2.0f, fixedpointbits);
float v = data[datastart + 1 + i * 6] / pow(2.0f, fixedpointbits);
group->push_back(Vec2(u, v));
}
break;
case 2: // fixed point u16
for (int i = 0; i < datacount; i++) {
float u = BigUInt16(data, datastart + i * 4) / pow(2.0f,
fixedpointbits);
float v = BigUInt16(data, datastart + 2 + i * 4) / pow(2.0f,
fixedpointbits);
group->push_back(Vec2(u, v));
}
case 3: // fixed point s16
for (int i = 0; i < datacount; i++) {
float u = BigSInt16(data, datastart + i * 4) / pow(2.0f,
fixedpointbits);
float v = BigSInt16(data, datastart + 2 + i * 4) / pow(2.0f,
fixedpointbits);
group->push_back(Vec2(u, v));
}
break;
case 4: // floating point
for (int i = 0; i < datacount; i++) {
float u = BigFloat(data, datastart + i * 8);
float v = BigFloat(data, datastart + 4 + i * 8);
group->push_back(Vec2(u, v));
}
break;
default:
return false;
}
return true;
}
bool MDL0File::loadRGBAGroup(const char* data, RGBAGroup* group) {
char datatype = data[27];
uint16_t datacount = BigUInt16(data, 30);
int datastart = 32;
switch (datatype) {
case 0: // RGB565
for (int i = 0; i < datacount; i++) {
char r = ((data[datastart + 2 * i] & 0xF8) >> 3) / 0x1F * 0xFF;
char g = (((data[datastart + 2 * i] & 0x7) << 3) | ((data[datastart
+ 1 + 2 * i] & 0xE0) >> 5)) / 0x3F * 0xFF;
char b = (data[datastart + 1 + 2 * i] & 0x1F) / 0x1F * 0xFF;
group->push_back(RGBA(r, g, b, 0xFF));
}
break;
case 1: // RGB8
for (int i = 0; i < datacount; i++) {
char r = data[datastart + 3 * i];
char g = data[datastart + 1 + 3 * i];
char b = data[datastart + 2 + 3 * i];
group->push_back(RGBA(r, g, b, 0xFF));
}
break;
case 2: // RGBX8
for (int i = 0; i < datacount; i++) {
char r = data[datastart + 4 * i];
char g = data[datastart + 1 + 4 * i];
char b = data[datastart + 2 + 4 * i];
group->push_back(RGBA(r, g, b, 0xFF));
}
break;
case 3: // RGBA4
for (int i = 0; i < datacount; i++) {
char r = (data[datastart + 2 * i] >> 4) * 0x11;
char g = data[datastart + 2 * i] * 0x11;
char b = (data[datastart + 1 + 2 * i] >> 4) * 0x11;
char a = data[datastart + 1 + 2 * i] * 0x11;
group->push_back(RGBA(r, g, b, a));
}
break;
case 4: // RGBA6
for (int i = 0; i < datacount; i++) {
char r = ((data[datastart + 3 * i] & 0x3F) >> 2) / 0x3F * 0xFF;
char g = (((data[datastart + 3 * i] & 0x3) << 4) | ((data[datastart
+ 1 + 3 * i] & 0xF0) >> 4)) / 0x3F * 0xFF;
char b = (((data[datastart + 1 + 3 * i] & 0xF) << 4)
| ((data[datastart + 2 + 3 * i] & 0xC0) >> 6)) / 0x3F
* 0xFF;
char a = (data[datastart + 2 + 3 * i] & 0x3F) / 0x3F * 0xFF;
group->push_back(RGBA(r, g, b, a));
}
break;
case 5: // RGBA8
for (int i = 0; i < datacount; i++) {
unsigned char r = data[datastart + 4 * i];
unsigned char g = data[datastart + 1 + 4 * i];
unsigned char b = data[datastart + 2 + 4 * i];
unsigned char a = data[datastart + 3 + 4 * i];
group->push_back(RGBA(r, g, b, a));
}
break;
default:
return false;
}
return true;
}
bool MDL0File::loadPolyGroup(const char* data, PolyGroup* group) {
uint16_t vertexid = BigUInt16(data, 72);
uint16_t normalid = BigUInt16(data, 74);
uint16_t colorid = BigUInt16(data, 76);
uint16_t texcoordid[8];
for (int i = 0; i < 8; i++)
texcoordid[i] = BigUInt16(data, 80 + 2 * i);
uint16_t unknowncount = BigUInt16(data, 102);
// find the start of the array format
int loc = 104 + unknowncount * 2;
while (data[loc] == 0) {
loc++;
}
// read array format
std::vector<int> fmt;
bool normalfmt = false;
bool colorfmt = false;
int unknownfmt = 0;
int uvfmt = 0;
int tempfmt = 0;
if (data[loc] != 0x08 && data[loc + 1] != 0x50)
return false;
tempfmt = data[loc + 5] & 3;
if (tempfmt) {
unknownfmt = 1;
}
tempfmt = (data[loc + 5] & 12) >> 2;
if (tempfmt) {
unknownfmt++;
}
tempfmt = (data[loc + 5] & 48) >> 4;
if (tempfmt) {
unknownfmt++;
}
tempfmt = (data[loc + 5] & 192) >> 6;
if (tempfmt) {
unknownfmt++;
}
tempfmt = (data[loc + 4] & 6) >> 1;
if (tempfmt)
fmt.push_back(tempfmt - 1);
tempfmt = (data[loc + 4] & 24) >> 3;
if (tempfmt)
fmt.push_back(tempfmt - 1);
tempfmt = (data[loc + 4] & 96) >> 5;
if (tempfmt)
fmt.push_back(tempfmt - 1);
if (data[loc + 6] != 0x08 && data[loc + 7] != 0x60)
return false;
tempfmt = data[loc + 11] & 3;
if (tempfmt)
fmt.push_back(tempfmt - 1);
tempfmt = (data[loc + 11] & 12) >> 2;
if (tempfmt)
fmt.push_back(tempfmt - 1);
tempfmt = (data[loc + 11] & 48) >> 4;
if (tempfmt)
fmt.push_back(tempfmt - 1);
tempfmt = (data[loc + 11] & 192) >> 6;
if (tempfmt)
fmt.push_back(tempfmt - 1);
tempfmt = data[loc + 10] & 3;
if (tempfmt)
fmt.push_back(tempfmt - 1);
tempfmt = (data[loc + 10] & 12) >> 2;
if (tempfmt)
fmt.push_back(tempfmt - 1);
tempfmt = (data[loc + 10] & 48) >> 4;
if (tempfmt)
fmt.push_back(tempfmt - 1);
tempfmt = (data[loc + 10] & 192) >> 6;
if (tempfmt)
fmt.push_back(tempfmt - 1);
if (data[loc + 16] != 0x08 && data[loc + 17] != 0x00)
return false;
colorfmt = data[loc + 20] & 3;
normalfmt = (data[loc + 20] & 12) >> 2;
uvfmt = (data[loc + 20] & 240) >> 4;
// find the start of the data
loc += 40;
while (data[loc] == 0) {
loc++;
}
// read data
bool reading = true;
do {
char type = data[loc];
loc++;
switch (type) {
case 0x30:
loc += 4;
break;
case 0x20:
loc += 4;
break;
case 0x28:
loc += 4;
break;
case 0x98: {
uint16_t count = BigUInt16(data, loc);
loc += 2;
if (options & LO_CONV_TO_TRIS) {
int length;
for (int i = 0; i < count - 2; i++) {
Vertex p1, p2, p3;
if (i % 2) {
p3 = loadVertex(data, loc, fmt, normalfmt, colorfmt,
unknownfmt, uvfmt, length);
loc += length;
p2 = loadVertex(data, loc, fmt, normalfmt, colorfmt,
unknownfmt, uvfmt, length);
loc += length;
p1 = loadVertex(data, loc, fmt, normalfmt, colorfmt,
unknownfmt, uvfmt, length);
loc -= length;
} else {
p1 = loadVertex(data, loc, fmt, normalfmt, colorfmt,
unknownfmt, uvfmt, length);
loc += length;
p2 = loadVertex(data, loc, fmt, normalfmt, colorfmt,
unknownfmt, uvfmt, length);
loc += length;
p3 = loadVertex(data, loc, fmt, normalfmt, colorfmt,
unknownfmt, uvfmt, length);
loc -= length;
}
Triangle tri(p1, p2, p3);
group->AddTriangle(tri);
}
loc += length * 2;
} else {
TriangleStrip strip;
for (int i = 0; i < count; i++) {
int length;
strip.Add(loadVertex(data, loc, fmt, normalfmt, colorfmt,
unknownfmt, uvfmt, length));
loc += length;
}
strip.Lock();
group->AddTriangleStrip(strip);
}
}
break;
case 0x90: {
uint16_t count = BigUInt16(data, loc);
loc += 2;
for (int i = 0; i < count; i += 3) {
int length;
Vertex p1 = loadVertex(data, loc, fmt, normalfmt, colorfmt,
unknownfmt, uvfmt, length);
loc += length;
Vertex p2 = loadVertex(data, loc, fmt, normalfmt, colorfmt,
unknownfmt, uvfmt, length);
loc += length;
Vertex p3 = loadVertex(data, loc, fmt, normalfmt, colorfmt,
unknownfmt, uvfmt, length);
loc += length;
Triangle tri(p1, p2, p3);
group->AddTriangle(tri);
}
}
break;
case 0xA0: {
uint16_t count = BigUInt16(data, loc);
loc += 2;
if (options & LO_CONV_TO_TRIS) {
int length;
Vertex origin = loadVertex(data, loc, fmt, normalfmt, colorfmt,
unknownfmt, uvfmt, length);
loc += length;
for (int i = 1; i < count - 1; i++) {
Vertex p1 = loadVertex(data, loc, fmt, normalfmt, colorfmt,
unknownfmt, uvfmt, length);
loc += length;
Vertex p2 = loadVertex(data, loc, fmt, normalfmt, colorfmt,
unknownfmt, uvfmt, length);
Triangle tri(origin, p1, p2);
group->AddTriangle(tri);
}
loc += length;
} else {
int length;
Vertex origin = loadVertex(data, loc, fmt, normalfmt, colorfmt,
unknownfmt, uvfmt, length);
loc += length;
TriangleFan fan(origin);
for (int i = 1; i < count; i++) {
fan.Add(loadVertex(data, loc, fmt, normalfmt, colorfmt,
unknownfmt, uvfmt, length));
loc += length;
}
fan.Lock();
group->AddTriangleFan(fan);
}
}
break;
case 0x80: {
uint16_t count = BigUInt16(data, loc);
loc += 2;
if (options & LO_CONV_TO_TRIS) {
for (int i = 0; i < count; i += 4) {
int length;
Vertex p1 = loadVertex(data, loc, fmt, normalfmt, colorfmt,
unknownfmt, uvfmt, length);
loc += length;
Vertex p2 = loadVertex(data, loc, fmt, normalfmt, colorfmt,
unknownfmt, uvfmt, length);
loc += length;
Vertex p3 = loadVertex(data, loc, fmt, normalfmt, colorfmt,
unknownfmt, uvfmt, length);
loc += length;
Vertex p4 = loadVertex(data, loc, fmt, normalfmt, colorfmt,
unknownfmt, uvfmt, length);
loc += length;
Triangle tri1(p1, p2, p3);
Triangle tri2(p1, p3, p4);
group->AddTriangle(tri1);
group->AddTriangle(tri2);
}
} else {
for (int i = 0; i < count; i += 4) {
int length;
Vertex p1 = loadVertex(data, loc, fmt, normalfmt, colorfmt,
unknownfmt, uvfmt, length);
loc += length;
Vertex p2 = loadVertex(data, loc, fmt, normalfmt, colorfmt,
unknownfmt, uvfmt, length);
loc += length;
Vertex p3 = loadVertex(data, loc, fmt, normalfmt, colorfmt,
unknownfmt, uvfmt, length);
loc += length;
Vertex p4 = loadVertex(data, loc, fmt, normalfmt, colorfmt,
unknownfmt, uvfmt, length);
loc += length;
Quad quad(p1, p2, p3, p4);
group->AddQuad(quad);
}
}
}
break;
case 0x00:
reading = false;
break;
default:
return false;
}
} while (reading);
group->SetVertex(vertexid, &vertexGroups[vertexid]);
if (normalid != 0xFFFF)
group->SetNormal(normalid, &normalGroups[normalid]);
if (colorid != 0xFFFF)
group->SetColor(colorid, &colorGroups[colorid]);
for (int i = 0; i < 8; i++) {
if (texcoordid[i] != 0xFFFF) {
group->AddUV(texcoordid[i], &uvGroups[texcoordid[i]]);
}
}
group->Lock();
return true;
}
Vertex MDL0File::loadVertex(const char* data, int offset, std::vector<int> fmt,
bool hasNormal, bool hasColor, int unknown, int uv, int& outLength) {
uint16_t vert = 0;
uint16_t norm = 0;
uint16_t color = 0;
uint16_t uvs[8];
int length = 0;
// assuming all unknowns are one byte long for now.
length += unknown;
if (fmt[0] == 1) {
vert = (unsigned char) data[offset + length];
length += 1;
} else {
vert = BigUInt16(data, offset + length);
length += 2;
}
if (hasNormal) {
if (fmt[1] == 1) {
norm = (unsigned char) data[offset + length];
length += 1;
} else {
norm = BigUInt16(data, offset + length);
length += 2;
}
}
if (hasColor) {
if (fmt[1 + hasNormal] == 1) {
color = (unsigned char) data[offset + length];
length += 1;
} else {
color = BigUInt16(data, offset + length);
length += 2;
}
}
for (int i = 0; i < uv; i++) {
if (fmt[1 + hasNormal + hasColor + i] == 1) {
uvs[i] = (unsigned char) data[offset + length];
length += 1;
} else {
uvs[i] = BigUInt16(data, offset + length);
length += 2;
}
}
Vertex outvert;
outvert.Set(vert, norm, color, uvs, uv);
outLength = length;
return outvert;
}
using namespace std;
class COLLADAExport {
public:
COLLADAExport();
virtual ~COLLADAExport();
bool Export(MDL0File* mdl0, const char* destfilename);
private:
void addMetadata(domCOLLADA* root);
void addLibraryGeometry(domCOLLADA* root, MDL0File* mdl0);
void addPositionArray(daeElement* mesh, Vec3Group* group, string geomName);
void addTexcoordArray(daeElement* mesh, Vec2Group* group, string geomName);
void addTriangleStrips(daeElement* mesh, PolyGroup* group, string name);
void addTriangleFans(daeElement* mesh, PolyGroup* group, string name);
void addTriangles(daeElement* mesh, PolyGroup* group, string name);
void addQuads(daeElement* mesh, PolyGroup* group, string name);
void addInputs(daeElement* parent, PolyGroup* group, string name);
string intToString(int num);
};
COLLADAExport::COLLADAExport() {
// TODO Auto-generated constructor stub
}
COLLADAExport::~COLLADAExport() {
// TODO Auto-generated destructor stub
}
bool COLLADAExport::Export(MDL0File* mdl0, const char* destfilename) {
DAE dae;
domCOLLADA* root = dae.add(destfilename);
if (root == NULL)
return false;
addMetadata(root);
addLibraryGeometry(root, mdl0);
dae.writeAll();
return true;
}
void COLLADAExport::addMetadata(domCOLLADA* root) {
daeElement* asset = root->add("asset");
// add contributer
daeElement* contributor = asset->add("contributor");
//daeElement* author = asset->add("author");
daeElement* authoring_tool = contributor->add("authoring_tool");
authoring_tool->setCharData("MDL0 to COLLADA converter");
daeElement* comments = contributor->add("comments");
comments->setCharData(
"This file was created by the mdl0converter, written by William Hahne. The copyright of this file belongs to the creator of the original input file.");
// get the current time and then format it correctly for XML
ostringstream strm;
time_t t = time(NULL);
struct tm* lt = localtime(&t);
strm << lt->tm_year + 1900 << "-" << lt->tm_mon << "-" << lt->tm_mday
<< "T" << lt->tm_hour << ":" << lt->tm_min << ":" << lt->tm_sec
<< "Z";
string current_time = strm.str();
// set created and modified time
daeElement* created = asset->add("created");
created->setCharData(current_time);
daeElement* modified = asset->add("modified");
modified->setCharData(current_time);
}
void COLLADAExport::addLibraryGeometry(domCOLLADA* root, MDL0File* mdl0) {
daeElement* library_geometries = root->add("library_geometries");
for (int i = 0; i < mdl0->PolygonGroupCount(); i++) {
PolyGroup* group = mdl0->GetPolygonGroup(i);
daeElement* geometry = library_geometries->add("geometry");
ostringstream strm;
strm << "Polygon" << i;
string name = strm.str();
geometry->setAttribute("id", name.c_str());
daeElement* mesh = geometry->add("mesh");
addPositionArray(mesh, group->VertexGroupPtr(), name + "-position");
addPositionArray(mesh, group->NormalGroupPtr(), name + "-normal");
for (int i = 0; i < group->UVGroupCount(); i++)
addTexcoordArray(mesh, group->UVGroupPtr(i), name + "-texcoord"
+ intToString(i));
daeElement* vertices = mesh->add("vertices");
string vertName(name + "-vertex");
vertices->setAttribute("id", vertName.c_str());
daeElement* input = vertices->add("input");
input->setAttribute("semantic", "POSITION");
string sourceName("#" + name + "-position");
input->setAttribute("source", sourceName.c_str());
if (group->TriangleStripCount() > 0)
addTriangleStrips(mesh, group, name);
if (group->TriangleFanCount() > 0)
addTriangleFans(mesh, group, name);
if (group->TriangleCount() > 0)
addTriangles(mesh, group, name);
}
// temporary until I (hopefully) find a better solution
daeElement* visual_scene = root->add("library_visual_scenes visual_scene");
visual_scene->setAttribute("id", "testscene");
for (int i = 0; i < mdl0->PolygonGroupCount(); i++) {
daeElement* instance_geometry = visual_scene->add(
"node instance_geometry");
string name("#Polygon" + intToString(i));
instance_geometry->setAttribute("url", name.c_str());
}
daeElement* instance_visual_scene =
root->add("scene instance_visual_scene");
instance_visual_scene->setAttribute("url", "#testscene");
}
void COLLADAExport::addPositionArray(daeElement* mesh, Vec3Group* group,
string posName) {
daeElement* source = mesh->add("source");
source->setAttribute("id", posName.c_str());
daeElement* float_array = source->add("float_array");
string arrayPosName(posName + "-array");
float_array->setAttribute("id", arrayPosName.c_str());
float_array->setAttribute("count", intToString(group->size() * 3).c_str());
ostringstream strm;
for (uint i = 0; i < group->size(); i++) {
Vec3 vec = (*group)[i];
strm << -vec.X() << " " << vec.Y() << " " << vec.Z() << "\n";
}
float_array->setCharData(strm.str());
daeElement* accessor = source->add("technique_common accessor");
string arrayPosSourceName("#" + arrayPosName);
accessor->setAttribute("source", arrayPosSourceName.c_str());
accessor->setAttribute("count", intToString(group->size()).c_str());
accessor->setAttribute("stride", "3");
daeElement* paramX = accessor->add("param");
paramX->setAttribute("name", "X");
paramX->setAttribute("type", "float");
daeElement* paramY = accessor->add("param");
paramY->setAttribute("name", "Y");
paramY->setAttribute("type", "float");
daeElement* paramZ = accessor->add("param");
paramZ->setAttribute("name", "Z");
paramZ->setAttribute("type", "float");
}
void COLLADAExport::addTexcoordArray(daeElement* mesh, Vec2Group* group,
string posName) {
daeElement* source = mesh->add("source");
source->setAttribute("id", posName.c_str());
daeElement* float_array = source->add("float_array");
string arrayPosName(posName + "-array");
float_array->setAttribute("id", arrayPosName.c_str());
float_array->setAttribute("count", intToString(group->size() * 2).c_str());
ostringstream strm;
for (uint i = 0; i < group->size(); i++) {
Vec2 vec = (*group)[i];
strm << vec.U() << " " << vec.V() << " ";
}
float_array->setCharData(strm.str());
daeElement* accessor = source->add("technique_common accessor");
string arrayPosSourceName("#" + arrayPosName);
accessor->setAttribute("source", arrayPosSourceName.c_str());
accessor->setAttribute("count", intToString(group->size()).c_str());
accessor->setAttribute("stride", "2");
daeElement* paramU = accessor->add("param");
paramU->setAttribute("name", "U");
paramU->setAttribute("type", "float");
daeElement* paramV = accessor->add("param");
paramV->setAttribute("name", "V");
paramV->setAttribute("type", "float");
}
void COLLADAExport::addTriangleStrips(daeElement* mesh, PolyGroup* group,
string name) {
daeElement* tristrips = mesh->add("tristrips");
tristrips->setAttribute("count",
intToString(group->TriangleStripCount()).c_str());
addInputs(tristrips, group, name);
for (uint i = 0; i < group->TriangleStripCount(); i++) {
daeElement* p = tristrips->add("p");
TriangleStrip strip = group->GetTriangleStrip(i);
ostringstream strm;
for (uint j = 0; j < strip.Count(); j++) {
Vertex vert = strip[j];
if (vert.GetVertex() >= group->VertexGroupPtr()->size())
std::cout << "WARNING: Tristrip vertex out of range in "
<< name << endl;
if (vert.GetNormal() >= group->NormalGroupPtr()->size())
std::cout << "WARNING: Tristrip normal out of range in "
<< name << endl;
strm << vert.GetVertex() << " " << vert.GetNormal() << " ";
for (int k = 0; k < vert.UVCount(); k++) {
strm << vert.GetUV(k) << " ";
}
}
p->setCharData(strm.str());
}
}
void COLLADAExport::addTriangleFans(daeElement* mesh, PolyGroup* group,
string name) {
daeElement* trifans = mesh->add("trifans");
trifans->setAttribute("count",
intToString(group->TriangleFanCount()).c_str());
addInputs(trifans, group, name);
for (uint i = 0; i < group->TriangleFanCount(); i++) {
daeElement* p = trifans->add("p");
TriangleFan fan = group->GetTriangleFan(i);
ostringstream strm;
Vertex origin = fan.GetOriginVertex();
strm << origin.GetVertex() << " " << origin.GetNormal() << " ";
for (uint j = 0; j < fan.Count(); j++) {
Vertex vert = fan[j];
if (vert.GetVertex() >= group->VertexGroupPtr()->size())
std::cout << "WARNING: Triangle Fan vertex out of range in "
<< name << endl;
if (vert.GetNormal() >= group->NormalGroupPtr()->size())
std::cout << "WARNING: Triangle Fan normal out of range in "
<< name << endl;
strm << vert.GetVertex() << " " << vert.GetNormal() << " ";
for (int k = 0; k < vert.UVCount(); k++) {
strm << vert.GetUV(k) << " ";
}
}
p->setCharData(strm.str());
}
}
void COLLADAExport::addTriangles(daeElement* mesh, PolyGroup* group,
string name) {
daeElement* tris = mesh->add("triangles");
tris->setAttribute("count", intToString(group->TriangleCount()).c_str());
addInputs(tris, group, name);
daeElement* p = tris->add("p");
ostringstream strm;
for (uint i = 0; i < group->TriangleCount(); i++) {
Triangle tri = group->GetTriangle(i);
for (int j = 0; j < 3; j++) {
Vertex vert = tri[j];
if (vert.GetVertex() >= group->VertexGroupPtr()->size())
std::cout << "TRIANGLE WARNING: Vertex out of range in "
<< name << endl;
if (vert.GetNormal() >= group->NormalGroupPtr()->size())
std::cout << "TRIANGLE WARNING: Normal out of range in "
<< name << endl;
strm << vert.GetVertex() << " " << vert.GetNormal() << " ";
for (int k = 0; k < vert.UVCount(); k++) {
strm << vert.GetUV(k) << " ";
}
}
strm << "\n";
}
p->setCharData(strm.str());
}
void COLLADAExport::addQuads(daeElement* mesh, PolyGroup* group, string name) {
}
void COLLADAExport::addInputs(daeElement* parent, PolyGroup* group, string name) {
daeElement* inputVert = parent->add("input");
inputVert->setAttribute("semantic", "VERTEX");
string vertSourceName("#" + name + "-vertex");
inputVert->setAttribute("source", vertSourceName.c_str());
inputVert->setAttribute("offset", "0");
daeElement* inputNorm = parent->add("input");
inputNorm->setAttribute("semantic", "NORMAL");
string normSourceName("#" + name + "-normal");
inputNorm->setAttribute("source", normSourceName.c_str());
inputNorm->setAttribute("offset", "1");
for (int i = 0; i < group->UVGroupCount(); i++) {
daeElement* inputTex = parent->add("input");
inputTex->setAttribute("semantic", "TEXCOORD");
string texSourceName("#" + name + "-texcoord" + intToString(i));
inputTex->setAttribute("source", texSourceName.c_str());
inputTex->setAttribute("offset", intToString(2 + i).c_str());
inputTex->setAttribute("set", "0");
}
}
string COLLADAExport::intToString(int num) {
ostringstream strm;
strm << num;
return strm.str();
}
using namespace std;
void printUsage() {
cout << "Usage:" << endl;
cout << "mdl0converter [-t] input_file output_file" << endl << endl;
cout << "Options:" << endl;
cout << " -t Convert all triangle strips, triangle fans, and quads to triangles." << endl << endl;
cout << "Copyright (C) 2008 William Hahne" << endl;
}
void printCredits() {
// todo: add credits
}
int main(int argc, char *argv[]) {
char* inputfile = NULL;
char* outputfile = NULL;
uint opts = 0;
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
switch (argv[i][1]) {
case 't':
opts |= LO_CONV_TO_TRIS;
break;
default:
cout << "Invalid option: " << argv[i] << endl;
printUsage();
return 0;
}
} else {
if (inputfile == NULL) {
inputfile = argv[i];
}
else if (inputfile != NULL && outputfile == NULL) {
outputfile = argv[i];
}
}
}
if (inputfile == NULL || outputfile == NULL) {
cout << "Not enough arguments." << endl;
printUsage();
return 0;
}
ifstream f;
f.open(inputfile, ios::binary | ios::in);
if (!f.good() || !f.is_open()) {
cout << "Error opening file." << endl << endl;
printUsage();
return 0;
}
f.seekg(0, ios::beg);
int begin = f.tellg();
f.seekg(0, ios::end);
int size = (int) f.tellg() - begin;
f.seekg(0, ios::beg);
char* data = new char[size];
f.read(data, size);
MDL0File file;
file.Load(data, size, opts);
// convert to collada
COLLADAExport daeExport;
outputfile = inputfile + ".DAE"
if (!daeExport.Export(&file, outputfile)) {
cout << "Error writing output file." << endl << endl;
printUsage();
return 0;
}
cout << "Success" << endl;
return 0;
}