Initial fbx model converter.

This commit is contained in:
lzwdgc 2017-07-28 03:06:37 +03:00
parent 98d993fa55
commit 6ce917f5e8
6 changed files with 454 additions and 101 deletions

View file

@ -27,8 +27,8 @@ if (DEFINED FBX_SDK_ROOT)
target_link_libraries(mod_converter
common
#pvt.cppan.demo.eigen
debug "${FBX_SDK_ROOT}/lib/vs2015/x86/debug/libfbxsdk.lib"
optimized "${FBX_SDK_ROOT}/lib/vs2015/x86/release/libfbxsdk.lib"
debug "${FBX_SDK_ROOT}/lib/vs2015/x86/debug/libfbxsdk-md.lib"
optimized "${FBX_SDK_ROOT}/lib/vs2015/x86/release/libfbxsdk-md.lib"
)
target_include_directories(mod_converter
PUBLIC "${FBX_SDK_ROOT}/include"

391
src/mod_converter/fbx.cpp Normal file
View file

@ -0,0 +1,391 @@
#include "fbx.h"
#include "model.h"
#include <fbxsdk.h>
#ifdef IOS_REF
#undef IOS_REF
#define IOS_REF (*(pManager->GetIOSettings()))
#endif
extern bool all_formats;
void InitializeSdkObjects(FbxManager*& pManager, FbxScene*& pScene)
{
//The first thing to do is to create the FBX Manager which is the object allocator for almost all the classes in the SDK
pManager = FbxManager::Create();
if (!pManager)
{
FBXSDK_printf("Error: Unable to create FBX Manager!\n");
exit(1);
}
//Create an IOSettings object. This object holds all import/export settings.
FbxIOSettings* ios = FbxIOSettings::Create(pManager, IOSROOT);
pManager->SetIOSettings(ios);
//Load plugins from the executable directory (optional)
FbxString lPath = FbxGetApplicationDirectory();
pManager->LoadPluginsDirectory(lPath.Buffer());
//Create an FBX scene. This object holds most objects imported/exported from/to files.
pScene = FbxScene::Create(pManager, "My Scene");
if (!pScene)
{
FBXSDK_printf("Error: Unable to create FBX scene!\n");
exit(1);
}
}
void DestroySdkObjects(FbxManager* pManager, bool pExitStatus)
{
//Delete the FBX Manager. All the objects that have been allocated using the FBX Manager and that haven't been explicitly destroyed are also automatically destroyed.
if (pManager) pManager->Destroy();
if (!pExitStatus)
FBXSDK_printf("Error: Unable to destroy FBX Manager!\n");
}
bool SaveScene(FbxManager* pManager, FbxDocument* pScene, const char* pFilename, bool pEmbedMedia, bool blender = false)
{
int lMajor, lMinor, lRevision;
bool lStatus = true;
// Create an exporter.
FbxExporter* lExporter = FbxExporter::Create(pManager, "");
auto pFileFormat = pManager->GetIOPluginRegistry()->GetNativeWriterFormat();
FbxString lDesc = pManager->GetIOPluginRegistry()->GetWriterFormatDescription(pFileFormat);
// Set the export states. By default, the export states are always set to
// true except for the option eEXPORT_TEXTURE_AS_EMBEDDED. The code below
// shows how to change these states.
IOS_REF.SetBoolProp(EXP_FBX_MATERIAL, true);
IOS_REF.SetBoolProp(EXP_FBX_TEXTURE, true);
IOS_REF.SetBoolProp(EXP_FBX_EMBEDDED, pEmbedMedia);
IOS_REF.SetBoolProp(EXP_FBX_SHAPE, true);
IOS_REF.SetBoolProp(EXP_FBX_GOBO, true);
IOS_REF.SetBoolProp(EXP_FBX_ANIMATION, true);
IOS_REF.SetBoolProp(EXP_FBX_GLOBAL_SETTINGS, true);
// Initialize the exporter by providing a filename.
if (lExporter->Initialize(pFilename, pFileFormat, pManager->GetIOSettings()) == false)
{
FBXSDK_printf("Call to FbxExporter::Initialize() failed.\n");
FBXSDK_printf("Error returned: %s\n\n", lExporter->GetStatus().GetErrorString());
return false;
}
// Export the scene.
// this one for blender
if (blender)
lExporter->SetFileExportVersion(FbxString("FBX201400"), FbxSceneRenamer::eNone);
lStatus = lExporter->Export(pScene);
// Destroy the exporter.
lExporter->Destroy();
return lStatus;
}
bool LoadScene(FbxManager* pManager, FbxDocument* pScene, const char* pFilename)
{
int lFileMajor, lFileMinor, lFileRevision;
int lSDKMajor, lSDKMinor, lSDKRevision;
//int lFileFormat = -1;
int i, lAnimStackCount;
bool lStatus;
char lPassword[1024];
// Get the file version number generate by the FBX SDK.
FbxManager::GetFileFormatVersion(lSDKMajor, lSDKMinor, lSDKRevision);
// Create an importer.
FbxImporter* lImporter = FbxImporter::Create(pManager, "");
// Initialize the importer by providing a filename.
const bool lImportStatus = lImporter->Initialize(pFilename, -1, pManager->GetIOSettings());
lImporter->GetFileVersion(lFileMajor, lFileMinor, lFileRevision);
if (!lImportStatus)
{
FbxString error = lImporter->GetStatus().GetErrorString();
FBXSDK_printf("Call to FbxImporter::Initialize() failed.\n");
FBXSDK_printf("Error returned: %s\n\n", error.Buffer());
if (lImporter->GetStatus().GetCode() == FbxStatus::eInvalidFileVersion)
{
FBXSDK_printf("FBX file format version for this FBX SDK is %d.%d.%d\n", lSDKMajor, lSDKMinor, lSDKRevision);
FBXSDK_printf("FBX file format version for file '%s' is %d.%d.%d\n\n", pFilename, lFileMajor, lFileMinor, lFileRevision);
}
return false;
}
FBXSDK_printf("FBX file format version for this FBX SDK is %d.%d.%d\n", lSDKMajor, lSDKMinor, lSDKRevision);
if (lImporter->IsFBX())
{
FBXSDK_printf("FBX file format version for file '%s' is %d.%d.%d\n\n", pFilename, lFileMajor, lFileMinor, lFileRevision);
// From this point, it is possible to access animation stack information without
// the expense of loading the entire file.
FBXSDK_printf("Animation Stack Information\n");
lAnimStackCount = lImporter->GetAnimStackCount();
FBXSDK_printf(" Number of Animation Stacks: %d\n", lAnimStackCount);
FBXSDK_printf(" Current Animation Stack: \"%s\"\n", lImporter->GetActiveAnimStackName().Buffer());
FBXSDK_printf("\n");
for (i = 0; i < lAnimStackCount; i++)
{
FbxTakeInfo* lTakeInfo = lImporter->GetTakeInfo(i);
FBXSDK_printf(" Animation Stack %d\n", i);
FBXSDK_printf(" Name: \"%s\"\n", lTakeInfo->mName.Buffer());
FBXSDK_printf(" Description: \"%s\"\n", lTakeInfo->mDescription.Buffer());
// Change the value of the import name if the animation stack should be imported
// under a different name.
FBXSDK_printf(" Import Name: \"%s\"\n", lTakeInfo->mImportName.Buffer());
// Set the value of the import state to false if the animation stack should be not
// be imported.
FBXSDK_printf(" Import State: %s\n", lTakeInfo->mSelect ? "true" : "false");
FBXSDK_printf("\n");
}
// Set the import states. By default, the import states are always set to
// true. The code below shows how to change these states.
IOS_REF.SetBoolProp(IMP_FBX_MATERIAL, true);
IOS_REF.SetBoolProp(IMP_FBX_TEXTURE, true);
IOS_REF.SetBoolProp(IMP_FBX_LINK, true);
IOS_REF.SetBoolProp(IMP_FBX_SHAPE, true);
IOS_REF.SetBoolProp(IMP_FBX_GOBO, true);
IOS_REF.SetBoolProp(IMP_FBX_ANIMATION, true);
IOS_REF.SetBoolProp(IMP_FBX_GLOBAL_SETTINGS, true);
}
// Import the scene.
lStatus = lImporter->Import(pScene);
if (lStatus == false && lImporter->GetStatus().GetCode() == FbxStatus::ePasswordError)
{
FBXSDK_printf("Please enter password: ");
lPassword[0] = '\0';
FBXSDK_CRT_SECURE_NO_WARNING_BEGIN
scanf("%s", lPassword);
FBXSDK_CRT_SECURE_NO_WARNING_END
FbxString lString(lPassword);
IOS_REF.SetStringProp(IMP_FBX_PASSWORD, lString);
IOS_REF.SetBoolProp(IMP_FBX_PASSWORD_ENABLE, true);
lStatus = lImporter->Import(pScene);
if (lStatus == false && lImporter->GetStatus().GetCode() == FbxStatus::ePasswordError)
{
FBXSDK_printf("\nPassword is wrong, import aborted.\n");
}
}
// Destroy the importer.
lImporter->Destroy();
return lStatus;
}
bool CreateScene(model &m, const std::string &name, FbxManager* pSdkManager, FbxScene* pScene);
void model::printFbx(const std::string &fn)
{
FbxManager* lSdkManager = NULL;
FbxScene* lScene = NULL;
// Prepare the FBX SDK.
InitializeSdkObjects(lSdkManager, lScene);
// Create the scene.
CreateScene(*this, fn, lSdkManager, lScene);
SaveScene(lSdkManager, lScene, (fn + "_ue4.fbx").c_str(), true);
if (all_formats)
SaveScene(lSdkManager, lScene, (fn + "_blender.fbx").c_str(), true, true);
// Destroy all objects created by the FBX SDK.
DestroySdkObjects(lSdkManager, true);
}
bool CreateScene(model &model, const std::string &name, FbxManager* pSdkManager, FbxScene* pScene)
{
static const char* gDiffuseElementName = "DiffuseUV";
static const char* gAmbientElementName = "AmbientUV";
static const char* gSpecularElementName = "SpecularUV";
for (auto &b : model.blocks)
{
if (!b.canPrint())
continue;
auto block_name = name + "/" + b.name;
// mesh
auto m = FbxMesh::Create(pSdkManager, block_name.c_str());
// node
FbxNode *node = FbxNode::Create(pScene, block_name.c_str());
node->SetNodeAttribute(m);
node->SetShadingMode(FbxNode::eTextureShading); // change?! was texture sh
// vertices
m->InitControlPoints(b.vertices.size());
// normals
auto normal = m->CreateElementNormal();
normal->SetMappingMode(FbxGeometryElement::eByControlPoint);
normal->SetReferenceMode(FbxGeometryElement::eDirect);
// uv
auto create_uv = [&m](auto &name)
{
auto uv1 = m->CreateElementUV(name);
uv1->SetMappingMode(FbxGeometryElement::eByControlPoint);
uv1->SetReferenceMode(FbxGeometryElement::eDirect);
return uv1;
};
auto d_uv = create_uv(gDiffuseElementName);
auto a_uv = create_uv(gAmbientElementName);
auto s_uv = create_uv(gSpecularElementName);
// add vertices, normals, uvs
int i = 0;
for (auto &v : b.vertices)
{
FbxVector4 x;
x.Set(-v.coordinates.x * scale_mult, v.coordinates.z * scale_mult, v.coordinates.y * scale_mult, v.coordinates.w);
m->SetControlPointAt(x, i++);
normal->GetDirectArray().Add(FbxVector4(-v.normal.x, -v.normal.z, v.normal.y));
float f;
auto uc = modf(fabs(v.texture_coordinates.u), &f);
auto vc = modf(fabs(v.texture_coordinates.v), &f);
d_uv->GetDirectArray().Add(FbxVector2(uc, 1 - vc));
a_uv->GetDirectArray().Add(FbxVector2(uc, 1 - vc));
s_uv->GetDirectArray().Add(FbxVector2(uc, 1 - vc));
}
// faces
for (auto &v : b.faces)
{
// Set the control point indices of the bottom side of the pyramid
m->BeginPolygon();
m->AddPolygon(v.x);
m->AddPolygon(v.z);
m->AddPolygon(v.y);
m->EndPolygon();
}
// mats
auto lMaterial = node->GetSrcObject<FbxSurfacePhong>(0);
if (lMaterial == NULL)
{
FbxString lMaterialName = block_name.c_str();
FbxString lShadingName = "Phong";
FbxDouble3 lAmbientColor(b.mat.ambient.x, b.mat.ambient.y, b.mat.ambient.z);
FbxDouble3 lSpecularColor(b.mat.specular.x, b.mat.specular.y, b.mat.specular.z);
FbxDouble3 lDiffuseColor(b.mat.diffuse.x, b.mat.diffuse.y, b.mat.diffuse.z);
FbxDouble3 lEmissiveColor(b.mat.emissive.x, b.mat.emissive.y, b.mat.emissive.z);
FbxLayer* lLayer = m->GetLayer(0);
// Create a layer element material to handle proper mapping.
FbxLayerElementMaterial* lLayerElementMaterial = FbxLayerElementMaterial::Create(m, lMaterialName.Buffer());
// This allows us to control where the materials are mapped. Using eAllSame
// means that all faces/polygons of the mesh will be assigned the same material.
lLayerElementMaterial->SetMappingMode(FbxLayerElement::eAllSame);
lLayerElementMaterial->SetReferenceMode(FbxLayerElement::eDirect);
// Save the material on the layer
lLayer->SetMaterials(lLayerElementMaterial);
// Add an index to the lLayerElementMaterial. Since we have only one, and are using eAllSame mapping mode,
// we only need to add one.
lLayerElementMaterial->GetIndexArray().Add(0);
lMaterial = FbxSurfacePhong::Create(pScene, lMaterialName.Buffer());
// Generate primary and secondary colors.
lMaterial->Emissive.Set(lEmissiveColor);
//lMaterial->EmissiveFactor.Set(b.material.emissive.x);
lMaterial->Ambient.Set(lAmbientColor);
//lMaterial->AmbientFactor.Set(b.mat.ambient.x);
// Add texture for diffuse channel
lMaterial->Diffuse.Set(lDiffuseColor);
//lMaterial->DiffuseFactor.Set(b.mat.diffuse.x);
//lMaterial->TransparencyFactor.Set(0.4);
//lMaterial->ShadingModel.Set(lShadingName);
//lMaterial->Shininess.Set(0.5);
lMaterial->Specular.Set(lSpecularColor);
lMaterial->SpecularFactor.Set(b.mat.power);
node->AddMaterial(lMaterial);
}
FbxFileTexture* lTexture;
// Set texture properties.
lTexture = FbxFileTexture::Create(pScene, "Diffuse Texture");
lTexture->SetFileName((b.tex_mask + ".TM.tga").c_str()); // Resource file is in current directory.
lTexture->SetTextureUse(FbxTexture::eStandard);
lTexture->SetMappingType(FbxTexture::eUV);
lTexture->SetMaterialUse(FbxFileTexture::eModelMaterial);
lTexture->UVSet.Set(gDiffuseElementName);
if (lMaterial)
lMaterial->Diffuse.ConnectSrcObject(lTexture);
// Set texture properties.
lTexture = FbxFileTexture::Create(pScene, "Ambient Texture");
lTexture->SetFileName((b.tex_mask + ".TM.tga").c_str()); // Resource file is in current directory.
lTexture->SetTextureUse(FbxTexture::eStandard);
lTexture->SetMappingType(FbxTexture::eUV);
lTexture->SetMaterialUse(FbxFileTexture::eModelMaterial);
lTexture->UVSet.Set(gAmbientElementName);
if (lMaterial)
lMaterial->Ambient.ConnectSrcObject(lTexture);
// Set texture properties.
lTexture = FbxFileTexture::Create(pScene, "Specular Texture");
lTexture->SetFileName((b.tex_spec + ".TM.tga").c_str()); // Resource file is in current directory.
lTexture->SetTextureUse(FbxTexture::eStandard);
lTexture->SetMappingType(FbxTexture::eUV);
lTexture->SetMaterialUse(FbxFileTexture::eModelMaterial);
lTexture->UVSet.Set(gSpecularElementName);
if (lMaterial)
lMaterial->Specular.ConnectSrcObject(lTexture);
// add smoothing groups
FbxGeometryConverter lGeometryConverter(pSdkManager);
lGeometryConverter.ComputeEdgeSmoothingFromNormals(m);
//convert soft/hard edge info to smoothing group info
lGeometryConverter.ComputePolygonSmoothingFromEdgeSmoothing(m);
//
pScene->GetRootNode()->AddChild(node);
}
return true;
}

1
src/mod_converter/fbx.h Normal file
View file

@ -0,0 +1 @@
#pragma once

View file

@ -29,47 +29,13 @@
using namespace std;
// options
bool all_formats = false;
bool silent = false;
bool printMaxPolygonBlock = false;
string filename;
bool parse_cmd(int argc, char *argv[]);
void print(const block &b, const std::string &fn)
{
if (b.type == BlockType::ParticleEmitter)
return;
auto obj_fn = fn;
if (!printMaxPolygonBlock)
obj_fn += string(".") + b.name;
obj_fn += ".obj";
ofstream o(obj_fn);
o << "#" << "\n";
o << "# A.I.M. Model Converter (ver. " << version() << ")\n";
o << "#" << "\n";
o << "\n";
int p1 = fn.rfind("\\");
int p2 = fn.rfind("/");
auto mtl = fn.substr(std::max(p1, p2) + 1);
if (!printMaxPolygonBlock)
mtl += string(".") + b.name;
o << "mtllib " << mtl << ".mtl\n";
o << "\n";
//o << b.printObj(mtl);
auto mtl_fn = fn;
if (!printMaxPolygonBlock)
mtl_fn += string(".") + b.name;
mtl_fn += ".mtl";
ofstream m(mtl_fn);
m << "#" << "\n";
m << "# A.I.M. Model Converter (ver. " << version() << ")\n";
m << "#" << "\n";
m << "\n";
//m << b.printMtl(mtl);
}
void convert_model(string fn)
{
buffer b(readFile(fn));
@ -84,30 +50,9 @@ void convert_model(string fn)
}
// write all
m.print(filename);
if (all_formats)
m.print(filename);
m.printFbx(filename);
return;
// write obj and mtl
if (printMaxPolygonBlock)
{
int max = 0;
int maxBlock = -1;
for (int i = 0; i < m.blocks.size(); i++)
{
if (m.blocks[i].n_vertex > max)
{
max = m.blocks[i].n_vertex;
maxBlock = i;
}
}
print(m.blocks[maxBlock], filename);
}
else
{
for (auto &f : m.blocks)
print(f, filename);
}
}
int main(int argc, char *argv[])
@ -166,6 +111,9 @@ bool parse_cmd(int argc, char *argv[])
}
switch (arg[1])
{
case 'a':
all_formats = true;
break;
case 's':
silent = true;
break;

View file

@ -36,6 +36,8 @@
using namespace std;
const float scale_mult = 30.0f;
const map<char, string> transliteration =
{
{ 'à',"a" },
@ -171,18 +173,18 @@ std::string vertex::printVertex(bool rotate_x_90) const
{
// that rotation is really equivalent to exchanging y and z and z sign
s = "v " +
to_string(-coordinates.x) + " " +
to_string(coordinates.z) + " " +
to_string(coordinates.y) + " " +
to_string(-coordinates.x * scale_mult) + " " +
to_string(coordinates.z * scale_mult) + " " +
to_string(coordinates.y * scale_mult) + " " +
to_string(coordinates.w)
;
}
else
{
s = "v " +
to_string(-coordinates.x) + " " +
to_string(coordinates.y) + " " +
to_string(-coordinates.z) + " " +
to_string(-coordinates.x * scale_mult) + " " +
to_string(coordinates.y * scale_mult) + " " +
to_string(-coordinates.z * scale_mult) + " " +
to_string(coordinates.w)
;
}
@ -211,6 +213,7 @@ std::string vertex::printTex() const
void damage_model::load(const buffer &b)
{
uint32_t n_polygons;
READ(b, n_polygons);
model_polygons.resize(n_polygons);
READ(b, unk8);
@ -219,13 +222,15 @@ void damage_model::load(const buffer &b)
READ(b, t);
READ(b, unk6);
READ(b, flags);
uint32_t n_vertex;
uint32_t n_faces;
READ(b, n_vertex);
vertices.resize(n_vertex);
READ(b, n_triangles);
damage_triangles.resize(n_triangles / 3);
READ(b, n_faces);
faces.resize(n_faces / 3);
for (auto &v : vertices)
v.load(b, flags);
for (auto &t : damage_triangles)
for (auto &t : faces)
READ(b, t);
}
@ -236,6 +241,18 @@ void material::load(const buffer &b)
READ(b, specular);
READ(b, emissive);
READ(b, power);
auto delim_by_3 = [](auto &v)
{
v.x /= 3.0f;
v.y /= 3.0f;
v.z /= 3.0f;
};
// in aim - those values lie in interval [0,3] instead of [0,1]
delim_by_3(diffuse);
delim_by_3(ambient);
delim_by_3(specular);
}
void animation::load(const buffer &b)
@ -273,15 +290,15 @@ void animation::segment::loadData(const buffer &b)
std::string block::printMtl() const
{
static const string ext = ".TM.bmp";
static const string ext = ".TM.tga";
string s;
s += "newmtl " + name + "\n";
s += "\n";
s += "Ka " + material.ambient.print() + "\n";
s += "Kd " + material.diffuse.print() + "\n";
s += "Ks " + material.specular.print() + "\n";
s += " Ns " + to_string(material.power) + "\n";
s += "Ka " + mat.ambient.print() + "\n";
s += "Kd " + mat.diffuse.print() + "\n";
s += "Ks " + mat.specular.print() + "\n";
s += " Ns " + to_string(mat.power) + "\n";
// d 1.0
// illum
s += "\n";
@ -316,18 +333,15 @@ std::string block::printObj(int group_offset, bool rotate_x_90) const
s += v.printTex() + "\n";
s += "\n";
if (n_faces)
for (auto &t : faces)
{
for (auto &t : faces)
{
auto x = to_string(t.x + 1 + group_offset);
auto y = to_string(t.y + 1 + group_offset);
auto z = to_string(t.z + 1 + group_offset);
x += "/" + x + "/" + x;
y += "/" + y + "/" + y;
z += "/" + z + "/" + z;
s += "f " + x + " " + z + " " + y + "\n";
}
auto x = to_string(t.x + 1 + group_offset);
auto y = to_string(t.y + 1 + group_offset);
auto z = to_string(t.z + 1 + group_offset);
x += "/" + x + "/" + x;
y += "/" + y + "/" + y;
z += "/" + z + "/" + z;
s += "f " + x + " " + z + " " + y + "\n";
}
s += "\n";
@ -361,9 +375,10 @@ void block::load(const buffer &b)
if (type == BlockType::ParticleEmitter)
return;
uint32_t n_animations;
READ(data, n_animations);
animations.resize(n_animations);
material.load(data);
mat.load(data);
// unk
READ(data, effect);
@ -379,10 +394,13 @@ void block::load(const buffer &b)
//
READ(data, additional_params);
uint32_t n_damage_models;
READ(data, n_damage_models);
damage_models.resize(n_damage_models);
READ(data, rot);
READ(data, flags);
uint32_t n_vertex;
uint32_t n_faces;
READ(data, n_vertex);
vertices.resize(n_vertex);
READ(data, n_faces);
@ -412,7 +430,7 @@ void block::load(const buffer &b)
{
// unknown end of block
auto triangles2 = faces;
triangles2.resize((data.size() - data.index()) / sizeof(triangle));
triangles2.resize((data.size() - data.index()) / sizeof(face));
for (auto &t : triangles2)
READ(data, t);
}
@ -433,9 +451,11 @@ bool block::canPrint() const
void model::load(const buffer &b)
{
int n_blocks;
READ(b, n_blocks);
if (n_blocks > 1000) // probably bad file
throw std::runtime_error("Model file has bad block count (should be <= 1000). Probably not a model.");
char header[0x40];
READ(b, header);
blocks.resize(n_blocks);
for (auto &f : blocks)
@ -465,7 +485,7 @@ void model::print(const std::string &fn)
continue;
o << b.printObj(n_vert, rotate_x_90) << "\n";
n_vert += b.n_vertex;
n_vert += b.vertices.size();
}
};

View file

@ -22,6 +22,8 @@
#include <string>
#include <vector>
extern const float scale_mult;
class buffer;
enum
@ -102,7 +104,7 @@ struct vertex
std::string printTex() const;
};
using triangle = aim_vector3<uint16_t>;
using face = aim_vector3<uint16_t>;
struct animation
{
@ -135,14 +137,11 @@ struct animation
struct damage_model
{
uint32_t n_polygons;
std::string name;
std::vector<uint16_t> model_polygons;
uint32_t flags;
uint32_t n_vertex;
uint32_t n_triangles;
std::vector<vertex> vertices;
std::vector<triangle> damage_triangles;
std::vector<face> faces;
uint8_t unk6;
float unk8[3];
@ -197,8 +196,7 @@ struct block
};
// data
uint32_t n_animations;
material material;
material mat;
//unk (anim + transform settings?)
EffectType effect;
@ -208,13 +206,10 @@ struct block
//
additional_parameters additional_params;
uint32_t n_damage_models;
rotation rot;
uint32_t flags;
uint32_t n_vertex;
uint32_t n_faces;
std::vector<vertex> vertices;
std::vector<triangle> faces;
std::vector<face> faces;
// animations
std::vector<animation> animations;
@ -243,8 +238,6 @@ struct block
struct model
{
int n_blocks;
char header[0x40];
std::vector<block> blocks;
void load(const buffer &b);