Files
BlitzNext/Runtime/blitz3d/loader_b3d.cpp
T
Michael Fabian 'Xaymar' Dirks 2196cb8419 runtime: Formatting
2019-01-18 17:04:17 +01:00

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