mirror of
https://github.com/aimrebirth/tools.git
synced 2026-04-15 01:43:25 +00:00
Initial fbx model converter.
This commit is contained in:
parent
98d993fa55
commit
6ce917f5e8
6 changed files with 454 additions and 101 deletions
|
|
@ -27,8 +27,8 @@ if (DEFINED FBX_SDK_ROOT)
|
||||||
target_link_libraries(mod_converter
|
target_link_libraries(mod_converter
|
||||||
common
|
common
|
||||||
#pvt.cppan.demo.eigen
|
#pvt.cppan.demo.eigen
|
||||||
debug "${FBX_SDK_ROOT}/lib/vs2015/x86/debug/libfbxsdk.lib"
|
debug "${FBX_SDK_ROOT}/lib/vs2015/x86/debug/libfbxsdk-md.lib"
|
||||||
optimized "${FBX_SDK_ROOT}/lib/vs2015/x86/release/libfbxsdk.lib"
|
optimized "${FBX_SDK_ROOT}/lib/vs2015/x86/release/libfbxsdk-md.lib"
|
||||||
)
|
)
|
||||||
target_include_directories(mod_converter
|
target_include_directories(mod_converter
|
||||||
PUBLIC "${FBX_SDK_ROOT}/include"
|
PUBLIC "${FBX_SDK_ROOT}/include"
|
||||||
|
|
|
||||||
391
src/mod_converter/fbx.cpp
Normal file
391
src/mod_converter/fbx.cpp
Normal 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
1
src/mod_converter/fbx.h
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
#pragma once
|
||||||
|
|
@ -29,47 +29,13 @@
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
// options
|
// options
|
||||||
|
bool all_formats = false;
|
||||||
bool silent = false;
|
bool silent = false;
|
||||||
bool printMaxPolygonBlock = false;
|
bool printMaxPolygonBlock = false;
|
||||||
string filename;
|
string filename;
|
||||||
|
|
||||||
bool parse_cmd(int argc, char *argv[]);
|
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)
|
void convert_model(string fn)
|
||||||
{
|
{
|
||||||
buffer b(readFile(fn));
|
buffer b(readFile(fn));
|
||||||
|
|
@ -84,30 +50,9 @@ void convert_model(string fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// write all
|
// write all
|
||||||
|
if (all_formats)
|
||||||
m.print(filename);
|
m.print(filename);
|
||||||
m.printFbx(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[])
|
int main(int argc, char *argv[])
|
||||||
|
|
@ -166,6 +111,9 @@ bool parse_cmd(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
switch (arg[1])
|
switch (arg[1])
|
||||||
{
|
{
|
||||||
|
case 'a':
|
||||||
|
all_formats = true;
|
||||||
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
silent = true;
|
silent = true;
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,8 @@
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
const float scale_mult = 30.0f;
|
||||||
|
|
||||||
const map<char, string> transliteration =
|
const map<char, string> transliteration =
|
||||||
{
|
{
|
||||||
{ 'à',"a" },
|
{ 'à',"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
|
// that rotation is really equivalent to exchanging y and z and z sign
|
||||||
s = "v " +
|
s = "v " +
|
||||||
to_string(-coordinates.x) + " " +
|
to_string(-coordinates.x * scale_mult) + " " +
|
||||||
to_string(coordinates.z) + " " +
|
to_string(coordinates.z * scale_mult) + " " +
|
||||||
to_string(coordinates.y) + " " +
|
to_string(coordinates.y * scale_mult) + " " +
|
||||||
to_string(coordinates.w)
|
to_string(coordinates.w)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
s = "v " +
|
s = "v " +
|
||||||
to_string(-coordinates.x) + " " +
|
to_string(-coordinates.x * scale_mult) + " " +
|
||||||
to_string(coordinates.y) + " " +
|
to_string(coordinates.y * scale_mult) + " " +
|
||||||
to_string(-coordinates.z) + " " +
|
to_string(-coordinates.z * scale_mult) + " " +
|
||||||
to_string(coordinates.w)
|
to_string(coordinates.w)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
@ -211,6 +213,7 @@ std::string vertex::printTex() const
|
||||||
|
|
||||||
void damage_model::load(const buffer &b)
|
void damage_model::load(const buffer &b)
|
||||||
{
|
{
|
||||||
|
uint32_t n_polygons;
|
||||||
READ(b, n_polygons);
|
READ(b, n_polygons);
|
||||||
model_polygons.resize(n_polygons);
|
model_polygons.resize(n_polygons);
|
||||||
READ(b, unk8);
|
READ(b, unk8);
|
||||||
|
|
@ -219,13 +222,15 @@ void damage_model::load(const buffer &b)
|
||||||
READ(b, t);
|
READ(b, t);
|
||||||
READ(b, unk6);
|
READ(b, unk6);
|
||||||
READ(b, flags);
|
READ(b, flags);
|
||||||
|
uint32_t n_vertex;
|
||||||
|
uint32_t n_faces;
|
||||||
READ(b, n_vertex);
|
READ(b, n_vertex);
|
||||||
vertices.resize(n_vertex);
|
vertices.resize(n_vertex);
|
||||||
READ(b, n_triangles);
|
READ(b, n_faces);
|
||||||
damage_triangles.resize(n_triangles / 3);
|
faces.resize(n_faces / 3);
|
||||||
for (auto &v : vertices)
|
for (auto &v : vertices)
|
||||||
v.load(b, flags);
|
v.load(b, flags);
|
||||||
for (auto &t : damage_triangles)
|
for (auto &t : faces)
|
||||||
READ(b, t);
|
READ(b, t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -236,6 +241,18 @@ void material::load(const buffer &b)
|
||||||
READ(b, specular);
|
READ(b, specular);
|
||||||
READ(b, emissive);
|
READ(b, emissive);
|
||||||
READ(b, power);
|
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)
|
void animation::load(const buffer &b)
|
||||||
|
|
@ -273,15 +290,15 @@ void animation::segment::loadData(const buffer &b)
|
||||||
|
|
||||||
std::string block::printMtl() const
|
std::string block::printMtl() const
|
||||||
{
|
{
|
||||||
static const string ext = ".TM.bmp";
|
static const string ext = ".TM.tga";
|
||||||
|
|
||||||
string s;
|
string s;
|
||||||
s += "newmtl " + name + "\n";
|
s += "newmtl " + name + "\n";
|
||||||
s += "\n";
|
s += "\n";
|
||||||
s += "Ka " + material.ambient.print() + "\n";
|
s += "Ka " + mat.ambient.print() + "\n";
|
||||||
s += "Kd " + material.diffuse.print() + "\n";
|
s += "Kd " + mat.diffuse.print() + "\n";
|
||||||
s += "Ks " + material.specular.print() + "\n";
|
s += "Ks " + mat.specular.print() + "\n";
|
||||||
s += " Ns " + to_string(material.power) + "\n";
|
s += " Ns " + to_string(mat.power) + "\n";
|
||||||
// d 1.0
|
// d 1.0
|
||||||
// illum
|
// illum
|
||||||
s += "\n";
|
s += "\n";
|
||||||
|
|
@ -316,8 +333,6 @@ std::string block::printObj(int group_offset, bool rotate_x_90) const
|
||||||
s += v.printTex() + "\n";
|
s += v.printTex() + "\n";
|
||||||
s += "\n";
|
s += "\n";
|
||||||
|
|
||||||
if (n_faces)
|
|
||||||
{
|
|
||||||
for (auto &t : faces)
|
for (auto &t : faces)
|
||||||
{
|
{
|
||||||
auto x = to_string(t.x + 1 + group_offset);
|
auto x = to_string(t.x + 1 + group_offset);
|
||||||
|
|
@ -328,7 +343,6 @@ std::string block::printObj(int group_offset, bool rotate_x_90) const
|
||||||
z += "/" + z + "/" + z;
|
z += "/" + z + "/" + z;
|
||||||
s += "f " + x + " " + z + " " + y + "\n";
|
s += "f " + x + " " + z + " " + y + "\n";
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
s += "\n";
|
s += "\n";
|
||||||
s += "\n";
|
s += "\n";
|
||||||
|
|
@ -361,9 +375,10 @@ void block::load(const buffer &b)
|
||||||
if (type == BlockType::ParticleEmitter)
|
if (type == BlockType::ParticleEmitter)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
uint32_t n_animations;
|
||||||
READ(data, n_animations);
|
READ(data, n_animations);
|
||||||
animations.resize(n_animations);
|
animations.resize(n_animations);
|
||||||
material.load(data);
|
mat.load(data);
|
||||||
|
|
||||||
// unk
|
// unk
|
||||||
READ(data, effect);
|
READ(data, effect);
|
||||||
|
|
@ -379,10 +394,13 @@ void block::load(const buffer &b)
|
||||||
//
|
//
|
||||||
|
|
||||||
READ(data, additional_params);
|
READ(data, additional_params);
|
||||||
|
uint32_t n_damage_models;
|
||||||
READ(data, n_damage_models);
|
READ(data, n_damage_models);
|
||||||
damage_models.resize(n_damage_models);
|
damage_models.resize(n_damage_models);
|
||||||
READ(data, rot);
|
READ(data, rot);
|
||||||
READ(data, flags);
|
READ(data, flags);
|
||||||
|
uint32_t n_vertex;
|
||||||
|
uint32_t n_faces;
|
||||||
READ(data, n_vertex);
|
READ(data, n_vertex);
|
||||||
vertices.resize(n_vertex);
|
vertices.resize(n_vertex);
|
||||||
READ(data, n_faces);
|
READ(data, n_faces);
|
||||||
|
|
@ -412,7 +430,7 @@ void block::load(const buffer &b)
|
||||||
{
|
{
|
||||||
// unknown end of block
|
// unknown end of block
|
||||||
auto triangles2 = faces;
|
auto triangles2 = faces;
|
||||||
triangles2.resize((data.size() - data.index()) / sizeof(triangle));
|
triangles2.resize((data.size() - data.index()) / sizeof(face));
|
||||||
for (auto &t : triangles2)
|
for (auto &t : triangles2)
|
||||||
READ(data, t);
|
READ(data, t);
|
||||||
}
|
}
|
||||||
|
|
@ -433,9 +451,11 @@ bool block::canPrint() const
|
||||||
|
|
||||||
void model::load(const buffer &b)
|
void model::load(const buffer &b)
|
||||||
{
|
{
|
||||||
|
int n_blocks;
|
||||||
READ(b, n_blocks);
|
READ(b, n_blocks);
|
||||||
if (n_blocks > 1000) // probably bad file
|
if (n_blocks > 1000) // probably bad file
|
||||||
throw std::runtime_error("Model file has bad block count (should be <= 1000). Probably not a model.");
|
throw std::runtime_error("Model file has bad block count (should be <= 1000). Probably not a model.");
|
||||||
|
char header[0x40];
|
||||||
READ(b, header);
|
READ(b, header);
|
||||||
blocks.resize(n_blocks);
|
blocks.resize(n_blocks);
|
||||||
for (auto &f : blocks)
|
for (auto &f : blocks)
|
||||||
|
|
@ -465,7 +485,7 @@ void model::print(const std::string &fn)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
o << b.printObj(n_vert, rotate_x_90) << "\n";
|
o << b.printObj(n_vert, rotate_x_90) << "\n";
|
||||||
n_vert += b.n_vertex;
|
n_vert += b.vertices.size();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
extern const float scale_mult;
|
||||||
|
|
||||||
class buffer;
|
class buffer;
|
||||||
|
|
||||||
enum
|
enum
|
||||||
|
|
@ -102,7 +104,7 @@ struct vertex
|
||||||
std::string printTex() const;
|
std::string printTex() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
using triangle = aim_vector3<uint16_t>;
|
using face = aim_vector3<uint16_t>;
|
||||||
|
|
||||||
struct animation
|
struct animation
|
||||||
{
|
{
|
||||||
|
|
@ -135,14 +137,11 @@ struct animation
|
||||||
|
|
||||||
struct damage_model
|
struct damage_model
|
||||||
{
|
{
|
||||||
uint32_t n_polygons;
|
|
||||||
std::string name;
|
std::string name;
|
||||||
std::vector<uint16_t> model_polygons;
|
std::vector<uint16_t> model_polygons;
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
uint32_t n_vertex;
|
|
||||||
uint32_t n_triangles;
|
|
||||||
std::vector<vertex> vertices;
|
std::vector<vertex> vertices;
|
||||||
std::vector<triangle> damage_triangles;
|
std::vector<face> faces;
|
||||||
|
|
||||||
uint8_t unk6;
|
uint8_t unk6;
|
||||||
float unk8[3];
|
float unk8[3];
|
||||||
|
|
@ -197,8 +196,7 @@ struct block
|
||||||
};
|
};
|
||||||
|
|
||||||
// data
|
// data
|
||||||
uint32_t n_animations;
|
material mat;
|
||||||
material material;
|
|
||||||
|
|
||||||
//unk (anim + transform settings?)
|
//unk (anim + transform settings?)
|
||||||
EffectType effect;
|
EffectType effect;
|
||||||
|
|
@ -208,13 +206,10 @@ struct block
|
||||||
//
|
//
|
||||||
|
|
||||||
additional_parameters additional_params;
|
additional_parameters additional_params;
|
||||||
uint32_t n_damage_models;
|
|
||||||
rotation rot;
|
rotation rot;
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
uint32_t n_vertex;
|
|
||||||
uint32_t n_faces;
|
|
||||||
std::vector<vertex> vertices;
|
std::vector<vertex> vertices;
|
||||||
std::vector<triangle> faces;
|
std::vector<face> faces;
|
||||||
|
|
||||||
// animations
|
// animations
|
||||||
std::vector<animation> animations;
|
std::vector<animation> animations;
|
||||||
|
|
@ -243,8 +238,6 @@ struct block
|
||||||
|
|
||||||
struct model
|
struct model
|
||||||
{
|
{
|
||||||
int n_blocks;
|
|
||||||
char header[0x40];
|
|
||||||
std::vector<block> blocks;
|
std::vector<block> blocks;
|
||||||
|
|
||||||
void load(const buffer &b);
|
void load(const buffer &b);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue