|
|
|
@ -0,0 +1,293 @@
|
|
|
|
|
#ifndef ASSIMP_BUILD_NO_FEZ_IMPORTER
|
|
|
|
|
|
|
|
|
|
#include "FezLoader.h"
|
|
|
|
|
#include <filesystem>
|
|
|
|
|
#include <assimp/Importer.hpp>
|
|
|
|
|
#include <assimp/DefaultLogger.hpp>
|
|
|
|
|
#include <assimp/importerdesc.h>
|
|
|
|
|
#include <assimp/ParsingUtils.h>
|
|
|
|
|
#include <assimp/XmlParser.h>
|
|
|
|
|
#include <assimp/IOSystem.hpp>
|
|
|
|
|
#include <assimp/scene.h>
|
|
|
|
|
|
|
|
|
|
namespace Assimp {
|
|
|
|
|
|
|
|
|
|
static const aiImporterDesc desc = {
|
|
|
|
|
"FEZ Importer",
|
|
|
|
|
"Kayden",
|
|
|
|
|
"",
|
|
|
|
|
"Unfinished.",
|
|
|
|
|
aiImporterFlags_Experimental | aiImporterFlags_LimitedSupport | aiImporterFlags_SupportTextFlavour,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
"xml",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const aiVector3D gc_normals[] = { {-1.f, 0.f, 0.f}, {0.f, -1.f, 0.f}, {0.f, 0.f, -1.f},
|
|
|
|
|
{1.f, 0.f, 0.f}, {0.f, 1.f, 0.f}, {0.f, 0.f, 1.f} };
|
|
|
|
|
|
|
|
|
|
static const ai_real gc_orient[] = {
|
|
|
|
|
AI_MATH_PI, -AI_MATH_HALF_PI, 0,AI_MATH_HALF_PI,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
FezLoader::FezLoader() = default;
|
|
|
|
|
FezLoader::~FezLoader() = default;
|
|
|
|
|
|
|
|
|
|
bool FezLoader::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool) const {
|
|
|
|
|
// TODO: Read single art objects too
|
|
|
|
|
static const char *tokens[] = { "<Level" };
|
|
|
|
|
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const aiImporterDesc *FezLoader::GetInfo() const {
|
|
|
|
|
return &desc;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void FezLoader::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
|
|
|
|
|
// Store filename
|
|
|
|
|
mFileName = pFile;
|
|
|
|
|
|
|
|
|
|
// Find asset directory (two levels up)
|
|
|
|
|
mFezAssetDir = std::filesystem::path(pFile).parent_path().parent_path();
|
|
|
|
|
|
|
|
|
|
// Open file for reading
|
|
|
|
|
std::unique_ptr<IOStream> levelFile;
|
|
|
|
|
levelFile.reset(pIOHandler->Open(pFile));
|
|
|
|
|
|
|
|
|
|
// generate a XML reader for it
|
|
|
|
|
if (!mLevelParser.parse(levelFile.get())) {
|
|
|
|
|
throw DeadlyImportError("Unable to read level file, malformed XML");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Load trile sets
|
|
|
|
|
auto levelRoot = mLevelParser.getRootNode().child("Level");
|
|
|
|
|
auto levelName = std::string(levelRoot.attribute("name").as_string());
|
|
|
|
|
|
|
|
|
|
// Fill in root node
|
|
|
|
|
pScene->mRootNode = new aiNode(levelName);
|
|
|
|
|
|
|
|
|
|
// Get trile set name in lowercase (all the files from export are lowercased)
|
|
|
|
|
auto trileSetName = std::string(levelRoot.attribute("trileSetName").as_string());
|
|
|
|
|
trileSetName = ai_tolower(trileSetName);
|
|
|
|
|
|
|
|
|
|
// Find and read trile set XML file
|
|
|
|
|
std::unique_ptr<IOStream> trileFile;
|
|
|
|
|
trileFile.reset(pIOHandler->Open(mFezAssetDir / "trile sets" / (trileSetName + ".xml")));
|
|
|
|
|
|
|
|
|
|
if (!mTrileParser.parse(trileFile.get())) {
|
|
|
|
|
throw DeadlyImportError("Unable to read trile file, malformed XML");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ASSIMP_LOG_VERBOSE_DEBUG("Parsed trile set ", mTrileParser.getRootNode().child("TrileSet").attribute("name").as_string());
|
|
|
|
|
|
|
|
|
|
// Place triles and their XML nodes into map
|
|
|
|
|
auto triles = mTrileParser.getRootNode().child("TrileSet").child("Triles");
|
|
|
|
|
for (auto t : triles.children()) {
|
|
|
|
|
if (std::string(t.name()) != "TrileEntry") {
|
|
|
|
|
ASSIMP_LOG_WARN("Unknown tag found in TrileSet XML.");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto key = t.attribute("key").as_int(-1);
|
|
|
|
|
if (key == -1) {
|
|
|
|
|
ASSIMP_LOG_ERROR("Trile found with an invalid key or non-int key. This could be bad!");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
auto trile = t.child("Trile");
|
|
|
|
|
if (mTriles.count(key)) {
|
|
|
|
|
ASSIMP_LOG_WARN("Duplicate trile key found, skipping...");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
mTriles[key] = trile;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ASSIMP_LOG_VERBOSE_DEBUG("Loaded ", mTriles.size(), " triles from the set.");
|
|
|
|
|
|
|
|
|
|
// Create children, one for the triles
|
|
|
|
|
auto trileRoot = new aiNode("Triles");
|
|
|
|
|
pScene->mRootNode->addChildren(1, &trileRoot);
|
|
|
|
|
|
|
|
|
|
// Begin filling out trile emplacements
|
|
|
|
|
auto xmlTrileT = levelRoot.child("Triles");
|
|
|
|
|
for (auto t : xmlTrileT.children()) {
|
|
|
|
|
if (std::string(t.name()) != "Entry") {
|
|
|
|
|
ASSIMP_LOG_WARN("Unknown tag found in Level Triles XML.");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto instance = t.child("TrileInstance");
|
|
|
|
|
if (instance.empty()) {
|
|
|
|
|
ASSIMP_LOG_WARN("Trile instance not found in a node.");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto trileID = instance.attribute("trileId").as_int(-1);
|
|
|
|
|
if (trileID == -1) {
|
|
|
|
|
ASSIMP_LOG_WARN("Trile instance found without a valid trileId!");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto meshID = GetTrileMeshFromId(trileID);
|
|
|
|
|
|
|
|
|
|
// Create node, link found mesh
|
|
|
|
|
auto trileNode = new aiNode();
|
|
|
|
|
trileNode->mNumMeshes = 1;
|
|
|
|
|
trileNode->mMeshes = new unsigned int[1];
|
|
|
|
|
trileNode->mMeshes[0] = meshID;
|
|
|
|
|
|
|
|
|
|
auto pos = GetFezVec3(instance.child("Position"));
|
|
|
|
|
auto orient = instance.attribute("orientation").as_int(0);
|
|
|
|
|
|
|
|
|
|
// Create transformation matrix
|
|
|
|
|
aiMatrix4x4 transform = aiMatrix4x4::Translation(pos, transform);
|
|
|
|
|
aiMatrix4x4 rotate = aiMatrix4x4::RotationY(gc_orient[orient], rotate);
|
|
|
|
|
|
|
|
|
|
trileNode->mTransformation = transform * rotate;
|
|
|
|
|
|
|
|
|
|
trileRoot->addChildren(1, &trileNode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Fill in meshes from vector
|
|
|
|
|
pScene->mNumMeshes = mTrileMeshes.size();
|
|
|
|
|
pScene->mMeshes = new aiMesh *[pScene->mNumMeshes];
|
|
|
|
|
for (int i = 0; i < mTrileMeshes.size(); i++) {
|
|
|
|
|
pScene->mMeshes[i] = mTrileMeshes[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create material
|
|
|
|
|
pScene->mNumMaterials = 1;
|
|
|
|
|
pScene->mMaterials = new aiMaterial * [1];
|
|
|
|
|
|
|
|
|
|
auto myMat = new aiMaterial();
|
|
|
|
|
aiString matName("TrileSet");
|
|
|
|
|
myMat->AddProperty(&matName, AI_MATKEY_NAME);
|
|
|
|
|
aiString texturePath(std::string(mFezAssetDir / "trile sets" / (trileSetName + ".png")));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Create embedded texture ref
|
|
|
|
|
auto trileTex = new aiTexture();
|
|
|
|
|
trileTex->mFilename = texturePath;
|
|
|
|
|
|
|
|
|
|
// PNG file, compressed
|
|
|
|
|
strcpy(trileTex->achFormatHint, "png");
|
|
|
|
|
trileTex->mHeight = 0;
|
|
|
|
|
|
|
|
|
|
// Open texture file for reading
|
|
|
|
|
std::unique_ptr<IOStream> texFile;
|
|
|
|
|
texFile.reset(pIOHandler->Open(mFezAssetDir / "trile sets" / (trileSetName + ".png")));
|
|
|
|
|
|
|
|
|
|
// Create buffer, read data, store into texture instance
|
|
|
|
|
auto texDat = new unsigned char[texFile->FileSize()];
|
|
|
|
|
trileTex->mWidth = texFile->FileSize();
|
|
|
|
|
texFile->Read(texDat, 1, texFile->FileSize());
|
|
|
|
|
trileTex->pcData = (aiTexel*)texDat;
|
|
|
|
|
|
|
|
|
|
// Store embedded texture into scene
|
|
|
|
|
pScene->mNumTextures = 1;
|
|
|
|
|
pScene->mTextures = new aiTexture *[1];
|
|
|
|
|
pScene->mTextures[0] = trileTex;
|
|
|
|
|
|
|
|
|
|
// Put ref into material
|
|
|
|
|
aiString tName("*0");
|
|
|
|
|
myMat->AddProperty(&tName, AI_MATKEY_TEXTURE(aiTextureType_BASE_COLOR, 0));
|
|
|
|
|
|
|
|
|
|
//store material in scene
|
|
|
|
|
pScene->mMaterials[0] = myMat;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int FezLoader::GetTrileMeshFromId(const int trileId) {
|
|
|
|
|
if (auto search = mTrileMap.find(trileId); search != mTrileMap.end()) {
|
|
|
|
|
return search->second;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We need to create an aiMesh instance and add it to the vector.
|
|
|
|
|
auto trileXML = mTriles.at(trileId);
|
|
|
|
|
auto mesh = new aiMesh();
|
|
|
|
|
|
|
|
|
|
auto xmlSIIP = trileXML.child("Geometry").child("ShaderInstancedIndexedPrimitives");
|
|
|
|
|
// TODO: check type="TriangleList"
|
|
|
|
|
|
|
|
|
|
auto xmlVerts = xmlSIIP.child("Vertices");
|
|
|
|
|
auto xmlIdx = xmlSIIP.child("Indices");
|
|
|
|
|
|
|
|
|
|
struct vpnti {
|
|
|
|
|
aiVector3D pos;
|
|
|
|
|
int normal;
|
|
|
|
|
aiVector3D texCoord;
|
|
|
|
|
};
|
|
|
|
|
std::vector<vpnti> vertex;
|
|
|
|
|
// TODO: check handedness/orientation
|
|
|
|
|
for (auto v : xmlVerts.children()) {
|
|
|
|
|
auto pos = GetFezVec3(v.child("Position"));
|
|
|
|
|
auto normal = v.child("Normal").text().as_int();
|
|
|
|
|
auto texCoord = GetFezVec3(v.child("TextureCoord").child("Vector2"), true);
|
|
|
|
|
texCoord.y = 1 - texCoord.y;
|
|
|
|
|
vertex.push_back({pos, normal, texCoord});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Collect indices into array
|
|
|
|
|
std::vector<unsigned int> idx;
|
|
|
|
|
for (auto i : xmlIdx.children()) {
|
|
|
|
|
auto id = i.text().as_uint();
|
|
|
|
|
idx.push_back(id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Populate mFaces with all triangles
|
|
|
|
|
mesh->mNumFaces = idx.size() / 3;
|
|
|
|
|
mesh->mFaces = new aiFace[mesh->mNumFaces];
|
|
|
|
|
// Iterate in sets of 3s.
|
|
|
|
|
for (int i = 0; i < idx.size(); i += 3) {
|
|
|
|
|
mesh->mFaces[i/3].mNumIndices = 3;
|
|
|
|
|
mesh->mFaces[i/3].mIndices = new unsigned int[3] {
|
|
|
|
|
idx.at(i),
|
|
|
|
|
idx.at(i+2),
|
|
|
|
|
idx.at(i+1),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Now populate vertices
|
|
|
|
|
mesh->mNumVertices = vertex.size();
|
|
|
|
|
mesh->mVertices = new aiVector3D[vertex.size()];
|
|
|
|
|
mesh->mNormals = new aiVector3D[vertex.size()];
|
|
|
|
|
mesh->mTextureCoords[0] = new aiVector3D[vertex.size()];
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < vertex.size(); i++) {
|
|
|
|
|
auto v = vertex.at(i);
|
|
|
|
|
mesh->mVertices[i] = v.pos;
|
|
|
|
|
mesh->mNormals[i] = gc_normals[v.normal];
|
|
|
|
|
mesh->mTextureCoords[0][i] = v.texCoord;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Only one material for all triles.
|
|
|
|
|
mesh->mMaterialIndex = 0;
|
|
|
|
|
|
|
|
|
|
// Add entry to trile map and add mesh to vector
|
|
|
|
|
mTrileMap[trileId] = mTrileMeshes.size();
|
|
|
|
|
mTrileMeshes.push_back(mesh);
|
|
|
|
|
|
|
|
|
|
return mTrileMap[trileId];
|
|
|
|
|
}
|
|
|
|
|
aiVector3D FezLoader::GetFezVec3(const XmlNode &t, bool direct) {
|
|
|
|
|
if (direct) {
|
|
|
|
|
return {t.attribute("x").as_float(), t.attribute("y").as_float(), t.attribute("z").as_float()};
|
|
|
|
|
} else {
|
|
|
|
|
auto c = t.child("Vector3");
|
|
|
|
|
return {c.attribute("x").as_float(), c.attribute("y").as_float(), c.attribute("z").as_float()};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
aiVector2D FezLoader::GetFezVec2(const XmlNode &t, bool direct) {
|
|
|
|
|
if (direct) {
|
|
|
|
|
return {t.attribute("x").as_float(), t.attribute("y").as_float()};
|
|
|
|
|
} else {
|
|
|
|
|
auto c = t.child("Vector2");
|
|
|
|
|
return {c.attribute("x").as_float(), c.attribute("y").as_float()};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|