#include "loader_b3d.hpp" #include "meshmodel.hpp" #include "meshutil.hpp" #include "pivot.hpp" #include "std.hpp" //#define SHOW_BONES static FILE* in; static vector chunk_stack; static vector textures; static vector brushes; static vector 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; }