409 lines
7.0 KiB
C++
409 lines
7.0 KiB
C++
|
|
#include "loader_b3d.hpp"
|
|
#include "meshmodel.hpp"
|
|
#include "meshutil.hpp"
|
|
#include "pivot.hpp"
|
|
#include "std.hpp"
|
|
|
|
//#define SHOW_BONES
|
|
|
|
static FILE* in;
|
|
static vector<int> chunk_stack;
|
|
static vector<Texture> textures;
|
|
static vector<Brush> brushes;
|
|
static vector<Object*> bones;
|
|
|
|
static bool collapse;
|
|
static bool animonly;
|
|
|
|
static int swap_endian(int n)
|
|
{
|
|
return ((n & 0xff) << 24) | ((n & 0xff00) << 8) | ((n & 0xff0000) >> 8) | ((n & 0xff000000) >> 24);
|
|
}
|
|
|
|
static void clear()
|
|
{
|
|
bones.clear();
|
|
brushes.clear();
|
|
textures.clear();
|
|
chunk_stack.clear();
|
|
}
|
|
|
|
static int readChunk()
|
|
{
|
|
int header[2];
|
|
if (fread(header, 8, 1, in) < 1)
|
|
return 0;
|
|
chunk_stack.push_back(ftell(in) + header[1]);
|
|
return swap_endian(header[0]);
|
|
}
|
|
|
|
static void exitChunk()
|
|
{
|
|
fseek(in, chunk_stack.back(), SEEK_SET);
|
|
chunk_stack.pop_back();
|
|
}
|
|
|
|
static int chunkSize()
|
|
{
|
|
return chunk_stack.back() - ftell(in);
|
|
}
|
|
|
|
static void read(void* buf, int n)
|
|
{
|
|
fread(buf, n, 1, in);
|
|
}
|
|
|
|
static void skip(int n)
|
|
{
|
|
fseek(in, n, SEEK_CUR);
|
|
}
|
|
|
|
static int readInt()
|
|
{
|
|
int n;
|
|
read(&n, 4);
|
|
return n;
|
|
}
|
|
|
|
static void readIntArray(int t[], int n)
|
|
{
|
|
read(t, n * 4);
|
|
}
|
|
|
|
static float readFloat()
|
|
{
|
|
float n;
|
|
read(&n, 4);
|
|
return n;
|
|
}
|
|
|
|
static void readFloatArray(float t[], int n)
|
|
{
|
|
read(t, n * 4);
|
|
}
|
|
|
|
static void readColor(unsigned* t)
|
|
{
|
|
float r = readFloat();
|
|
if (r < 0)
|
|
r = 0;
|
|
else if (r > 1)
|
|
r = 1;
|
|
float g = readFloat();
|
|
if (g < 0)
|
|
g = 0;
|
|
else if (g > 1)
|
|
g = 1;
|
|
float b = readFloat();
|
|
if (b < 0)
|
|
b = 0;
|
|
else if (b > 1)
|
|
b = 1;
|
|
float a = readFloat();
|
|
if (a < 0)
|
|
a = 0;
|
|
else if (a > 1)
|
|
a = 1;
|
|
*t = (int(a * 255) << 24) | (int(r * 255) << 16) | (int(g * 255) << 8) | int(b * 255);
|
|
}
|
|
|
|
static string readString()
|
|
{
|
|
string t;
|
|
for (;;) {
|
|
char c;
|
|
read(&c, 1);
|
|
if (!c)
|
|
return t;
|
|
t += c;
|
|
}
|
|
}
|
|
|
|
static void readTextures()
|
|
{
|
|
while (chunkSize()) {
|
|
string name = readString();
|
|
int flags = readInt();
|
|
int blend = readInt();
|
|
float pos[2], scl[2];
|
|
readFloatArray(pos, 2);
|
|
readFloatArray(scl, 2);
|
|
float rot = readFloat();
|
|
|
|
//create texture
|
|
Texture tex(name, flags & 0xffff);
|
|
|
|
tex.setBlend(blend);
|
|
if (flags & 0x10000)
|
|
tex.setFlags(gxScene::TEX_COORDS2);
|
|
|
|
if (pos[0] != 0 || pos[1] != 0)
|
|
tex.setPosition(pos[0], pos[1]);
|
|
if (scl[0] != 1 || scl[1] != 1)
|
|
tex.setScale(scl[0], scl[1]);
|
|
if (rot != 0)
|
|
tex.setRotation(rot);
|
|
|
|
textures.push_back(tex);
|
|
}
|
|
}
|
|
|
|
static void readBrushes()
|
|
{
|
|
int n_texs = readInt();
|
|
|
|
int tex_id[8] = {-1, -1, -1, -1, -1, -1, -1, -1};
|
|
|
|
while (chunkSize()) {
|
|
string name = readString();
|
|
float col[4];
|
|
readFloatArray(col, 4);
|
|
float shi = readFloat();
|
|
int blend = readInt();
|
|
int fx = readInt();
|
|
readIntArray(tex_id, n_texs);
|
|
|
|
Brush bru;
|
|
|
|
bru.setColor(Vector(col[0], col[1], col[2]));
|
|
bru.setAlpha(col[3]);
|
|
bru.setShininess(shi);
|
|
bru.setBlend(blend);
|
|
bru.setFX(fx);
|
|
|
|
for (int k = 0; k < 8; ++k) {
|
|
if (tex_id[k] < 0)
|
|
continue;
|
|
bru.setTexture(k, textures[tex_id[k]], 0);
|
|
}
|
|
|
|
brushes.push_back(bru);
|
|
}
|
|
}
|
|
|
|
static int readVertices()
|
|
{
|
|
int flags = readInt();
|
|
int tc_sets = readInt();
|
|
int tc_size = readInt();
|
|
|
|
float tc[4] = {0};
|
|
|
|
Surface::Vertex t;
|
|
while (chunkSize()) {
|
|
readFloatArray(t.coords, 3);
|
|
if (flags & 1) {
|
|
readFloatArray(t.normal, 3);
|
|
}
|
|
if (flags & 2) {
|
|
readColor(&t.color);
|
|
}
|
|
for (int k = 0; k < tc_sets; ++k) {
|
|
readFloatArray(tc, tc_size);
|
|
if (k < 2)
|
|
memcpy(t.tex_coords[k], tc, 8);
|
|
}
|
|
MeshLoader::addVertex(t);
|
|
}
|
|
|
|
return flags;
|
|
}
|
|
|
|
static void readTriangles()
|
|
{
|
|
int brush_id = readInt();
|
|
Brush b = brush_id >= 0 ? brushes[brush_id] : Brush();
|
|
while (chunkSize()) {
|
|
int verts[3];
|
|
readIntArray(verts, 3);
|
|
MeshLoader::addTriangle(verts, b);
|
|
}
|
|
}
|
|
|
|
static int readMesh()
|
|
{
|
|
int flags = 0;
|
|
while (chunkSize()) {
|
|
switch (readChunk()) {
|
|
case 'VRTS':
|
|
flags = readVertices();
|
|
break;
|
|
case 'TRIS':
|
|
readTriangles();
|
|
break;
|
|
}
|
|
exitChunk();
|
|
}
|
|
return flags;
|
|
}
|
|
|
|
static Object* readBone()
|
|
{
|
|
#ifdef SHOW_BONES
|
|
Brush b;
|
|
b.setColor(Vector(1, 0, 0));
|
|
b.setAlpha(.75f);
|
|
MeshModel* bone = MeshUtil::createSphere(b, 16);
|
|
Transform t;
|
|
t.m.i.x = .1f;
|
|
t.m.j.y = .1f;
|
|
t.m.k.z = .1f;
|
|
bone->transform(t);
|
|
#else
|
|
Pivot* bone = new Pivot();
|
|
#endif
|
|
|
|
bones.push_back(bone);
|
|
|
|
while (chunkSize()) {
|
|
int vert = readInt();
|
|
float weight = readFloat();
|
|
MeshLoader::addBone(vert, weight, bones.size());
|
|
}
|
|
return bone;
|
|
}
|
|
|
|
static void readKeys(Animation& anim)
|
|
{
|
|
int flags = readInt();
|
|
while (chunkSize()) {
|
|
int frame = readInt();
|
|
if (flags & 1) {
|
|
float pos[3];
|
|
readFloatArray(pos, 3);
|
|
anim.setPositionKey(frame, Vector(pos[0], pos[1], pos[2]));
|
|
}
|
|
if (flags & 2) {
|
|
float scl[3];
|
|
readFloatArray(scl, 3);
|
|
anim.setScaleKey(frame, Vector(scl[0], scl[1], scl[2]));
|
|
}
|
|
if (flags & 4) {
|
|
float rot[4];
|
|
readFloatArray(rot, 4);
|
|
anim.setRotationKey(frame, Quat(rot[0], Vector(rot[1], rot[2], rot[3])));
|
|
}
|
|
}
|
|
}
|
|
|
|
static Object* readObject(Object* parent)
|
|
{
|
|
Object* obj = 0;
|
|
|
|
string name = readString();
|
|
float pos[3], scl[3], rot[4];
|
|
readFloatArray(pos, 3);
|
|
readFloatArray(scl, 3);
|
|
readFloatArray(rot, 4);
|
|
|
|
Animation keys;
|
|
int anim_len = 0;
|
|
MeshModel* mesh = 0;
|
|
int mesh_flags, mesh_brush;
|
|
|
|
while (chunkSize()) {
|
|
switch (readChunk()) {
|
|
case 'MESH':
|
|
MeshLoader::beginMesh();
|
|
obj = mesh = new MeshModel();
|
|
mesh_brush = readInt();
|
|
mesh_flags = readMesh();
|
|
break;
|
|
case 'BONE':
|
|
obj = readBone();
|
|
break;
|
|
case 'KEYS':
|
|
readKeys(keys);
|
|
break;
|
|
case 'ANIM':
|
|
readInt();
|
|
anim_len = readInt();
|
|
readFloat();
|
|
break;
|
|
case 'NODE':
|
|
if (!obj)
|
|
obj = new MeshModel();
|
|
readObject(obj);
|
|
break;
|
|
}
|
|
exitChunk();
|
|
}
|
|
|
|
if (!obj)
|
|
obj = new MeshModel();
|
|
|
|
obj->SetName(name);
|
|
obj->SetLocalPosition(Vector(pos[0], pos[1], pos[2]));
|
|
obj->SetLocalScale(Vector(scl[0], scl[1], scl[2]));
|
|
obj->SetLocalRotation(Quat(rot[0], Vector(rot[1], rot[2], rot[3])));
|
|
obj->setAnimation(keys);
|
|
|
|
if (mesh) {
|
|
MeshLoader::endMesh(mesh);
|
|
if (!(mesh_flags & 1))
|
|
mesh->updateNormals();
|
|
if (mesh_brush != -1)
|
|
mesh->setBrush(brushes[mesh_brush]);
|
|
}
|
|
|
|
if (mesh && bones.size()) {
|
|
bones.insert(bones.begin(), mesh);
|
|
mesh->setAnimator(new Animator(bones, anim_len));
|
|
mesh->createBones();
|
|
bones.clear();
|
|
} else if (anim_len) {
|
|
obj->setAnimator(new Animator(obj, anim_len));
|
|
}
|
|
|
|
if (parent)
|
|
obj->SetParent(parent);
|
|
|
|
return obj;
|
|
}
|
|
|
|
MeshModel* Loader_B3D::load(const string& f, const Transform& conv, int hint)
|
|
{
|
|
collapse = !!(hint & MeshLoader::HINT_COLLAPSE);
|
|
animonly = !!(hint & MeshLoader::HINT_ANIMONLY);
|
|
|
|
in = fopen(f.c_str(), "rb");
|
|
if (!in)
|
|
return 0;
|
|
|
|
::clear();
|
|
|
|
int tag = readChunk();
|
|
if (tag != 'BB3D') {
|
|
fclose(in);
|
|
return 0;
|
|
}
|
|
|
|
int version = readInt();
|
|
if (version > 1) {
|
|
fclose(in);
|
|
return 0;
|
|
}
|
|
|
|
Object* obj = 0;
|
|
while (chunkSize()) {
|
|
switch (readChunk()) {
|
|
case 'TEXS':
|
|
readTextures();
|
|
break;
|
|
case 'BRUS':
|
|
readBrushes();
|
|
break;
|
|
case 'NODE':
|
|
obj = readObject(0);
|
|
break;
|
|
}
|
|
exitChunk();
|
|
}
|
|
fclose(in);
|
|
|
|
::clear();
|
|
|
|
return obj ? obj->getModel()->getMeshModel() : 0;
|
|
}
|