// Copyright (C) 2009-2015, Panagiotis Christopoulos Charitos. // All rights reserved. // Code licensed under the BSD License. // http://www.anki3d.org/LICENSE #include "Exporter.h" #include //============================================================================== // Statics = //============================================================================== static const char* XML_HEADER = R"()"; //============================================================================== static aiColor3D srgbToLinear(aiColor3D in) { const float p = 1.0 / 2.4; aiColor3D out; out[0] = pow(in[0], p); out[1] = pow(in[1], p); out[2] = pow(in[2], p); out[3] = in[3]; return out; } //============================================================================== /// Convert from sRGB to linear and preserve energy static aiColor3D computeLightColor(aiColor3D in) { float energy = std::max(std::max(in[0], in[1]), in[2]); if(energy > 1.0) { in[0] /= energy; in[1] /= energy; in[2] /= energy; } else { energy = 1.0; } in = srgbToLinear(in); in[0] *= energy; in[1] *= energy; in[2] *= energy; return in; } //============================================================================== /// Round up the instances count. static uint32_t roundUpInstancesCount(uint32_t instances) { if(instances == 1) { instances = 1; } else if(instances <= 4) { instances = 4; } else if(instances <= 8) { instances = 8; } else if(instances <= 16) { instances = 16; } else if(instances <= 32) { instances = 32; } else { ERROR("Too many instances %u", instances); } return instances; } //============================================================================== static std::string getMaterialName(const aiMaterial& mtl, uint32_t instances) { aiString ainame; std::string name; if(mtl.Get(AI_MATKEY_NAME, ainame) == AI_SUCCESS) { name = ainame.C_Str(); if(instances > 1) { name += "_inst" + std::to_string(roundUpInstancesCount(instances)); } } else { ERROR("Material's name is missing"); } return name; } //============================================================================== static std::string getMeshName(const aiMesh& mesh) { return std::string(mesh.mName.C_Str()); } //============================================================================== /// Walk the node hierarchy and find the node. static const aiNode* findNodeWithName( const std::string& name, const aiNode* node) { if(node == nullptr || node->mName.C_Str() == name) { return node; } const aiNode* out = nullptr; // Go to children for(uint32_t i = 0; i < node->mNumChildren; i++) { out = findNodeWithName(name, node->mChildren[i]); if(out) { break; } } return out; } //============================================================================== static std::vector tokenize(const std::string &source) { const char *delimiter = " "; bool keepEmpty = false; std::vector results; size_t prev = 0; size_t next = 0; while((next = source.find_first_of(delimiter, prev)) != std::string::npos) { if(keepEmpty || (next - prev != 0)) { results.push_back(source.substr(prev, next - prev)); } prev = next + 1; } if(prev < source.size()) { results.push_back(source.substr(prev)); } return results; } //============================================================================== template static void stringToFloatArray(const std::string& in, Arr& out) { std::vector tokens = tokenize(in); if(tokens.size() != N) { ERROR("Failed to parse %s", in.c_str()); } int count = 0; for(const std::string& s : tokens) { out[count] = std::stof(s); ++count; } } //============================================================================== // Exporter = //============================================================================== //============================================================================== aiMatrix4x4 Exporter::toAnkiMatrix(const aiMatrix4x4& in) const { static const aiMatrix4x4 toLeftHanded( 1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1); static const aiMatrix4x4 toLeftHandedInv( 1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1); if(m_flipyz) { return toLeftHanded * in * toLeftHandedInv; } else { return in; } } //============================================================================== aiMatrix3x3 Exporter::toAnkiMatrix(const aiMatrix3x3& in) const { static const aiMatrix3x3 toLeftHanded( 1, 0, 0, 0, 0, 1, 0, -1, 0); static const aiMatrix3x3 toLeftHandedInv( 1, 0, 0, 0, 0, -1, 0, 1, 0); if(m_flipyz) { return toLeftHanded * in; } else { return in; } } //============================================================================== void Exporter::writeTransform(const aiMatrix4x4& mat) { std::ofstream& file = m_sceneFile; aiMatrix4x4 m = toAnkiMatrix(mat); float pos[3]; pos[0] = m[0][3]; pos[1] = m[1][3]; pos[2] = m[2][3]; file << "trf = Transform.new()\n"; file << "trf:setOrigin(Vec4.new(" << pos[0] << ", " << pos[1] << ", " << pos[2] << ", 0))\n"; file << "rot = Mat3x4.new()\n"; file << "rot:setAll("; for(unsigned j = 0; j < 3; j++) { for(unsigned i = 0; i < 4; i++) { if(i == 3) { file << "0"; } else { file << m[j][i]; } if(!(i == 3 && j == 2)) { file << ", "; } } } file << ")\n"; file << "trf:setRotation(rot)\n"; file << "trf:setScale(1.0)\n"; } //============================================================================== void Exporter::writeNodeTransform( const std::string& node, const aiMatrix4x4& mat) { std::ofstream& file = m_sceneFile; writeTransform(mat); file << node << ":getSceneNodeBase():getMoveComponent():setLocalTransform(trf)\n"; } //============================================================================== const aiMesh& Exporter::getMeshAt(unsigned index) const { assert(index < m_scene->mNumMeshes); return *m_scene->mMeshes[index]; } //============================================================================== const aiMaterial& Exporter::getMaterialAt(unsigned index) const { assert(index < m_scene->mNumMaterials); return *m_scene->mMaterials[index]; } //============================================================================== std::string Exporter::getModelName(const Model& model) const { std::string name = getMeshName(getMeshAt(model.m_meshIndex)); name += getMaterialName( getMaterialAt(model.m_materialIndex), model.m_instancesCount); return name; } //============================================================================== void Exporter::exportSkeleton(const aiMesh& mesh) const { assert(mesh.HasBones()); std::string name = mesh.mName.C_Str(); std::fstream file; LOGI("Exporting skeleton %s", name.c_str()); // Open file file.open(m_outputDirectory + name + ".skel", std::ios::out); file << XML_HEADER << "\n"; file << "\n"; file << "\t\n"; bool rootBoneFound = false; for(uint32_t i = 0; i < mesh.mNumBones; i++) { const aiBone& bone = *mesh.mBones[i]; file << "\t\t\n"; // file << "\t\t\t" << bone.mName.C_Str() << "\n"; if(strcmp(bone.mName.C_Str(), "root") == 0) { rootBoneFound = true; } // file << "\t\t\t"; for(uint32_t j = 0; j < 16; j++) { file << bone.mOffsetMatrix[j] << " "; } file << "\n"; file << "\t\t\n"; } if(!rootBoneFound) { ERROR("There should be one bone named \"root\""); } file << "\t\n"; file << "\n"; } //============================================================================== void Exporter::exportMaterial( const aiMaterial& mtl, uint32_t instances) const { std::string diffTex; std::string normTex; std::string specColTex; std::string shininessTex; std::string dispTex; aiString path; std::string name = getMaterialName(mtl, instances); LOGI("Exporting material %s", name.c_str()); // Diffuse texture if(mtl.GetTextureCount(aiTextureType_DIFFUSE) > 0) { if(mtl.GetTexture(aiTextureType_DIFFUSE, 0, &path) == AI_SUCCESS) { diffTex = getFilename(path.C_Str()); } else { ERROR("Failed to retrieve texture"); } } // Normal texture if(mtl.GetTextureCount(aiTextureType_NORMALS) > 0) { if(mtl.GetTexture(aiTextureType_NORMALS, 0, &path) == AI_SUCCESS) { normTex = getFilename(path.C_Str()); } else { ERROR("Failed to retrieve texture"); } } // Specular color if(mtl.GetTextureCount(aiTextureType_SPECULAR) > 0) { if(mtl.GetTexture(aiTextureType_SPECULAR, 0, &path) == AI_SUCCESS) { specColTex = getFilename(path.C_Str()); } else { ERROR("Failed to retrieve texture"); } } // Shininess color if(mtl.GetTextureCount(aiTextureType_SHININESS) > 0) { if(mtl.GetTexture(aiTextureType_SHININESS, 0, &path) == AI_SUCCESS) { shininessTex = getFilename(path.C_Str()); } else { ERROR("Failed to retrieve texture"); } } // Height texture if(mtl.GetTextureCount(aiTextureType_DISPLACEMENT) > 0) { if(mtl.GetTexture(aiTextureType_DISPLACEMENT, 0, &path) == AI_SUCCESS) { dispTex = getFilename(path.C_Str()); } else { ERROR("Failed to retrieve texture"); } } // Write file static const char* diffNormSpecFragTemplate = #include "templates/diffNormSpecFrag.h" ; static const char* simpleVertTemplate = #include "templates/simpleVert.h" ; static const char* tessVertTemplate = #include "templates/tessVert.h" ; static const char* readRgbFromTextureTemplate = R"( %id% vec3 readRgbFromTexture %map% out2 )"; static const char* readRFromTextureTemplate = R"( %id% float readRFromTexture %map% out2 )"; // Compose full template // First geometry part std::string materialStr; materialStr = R"()"; materialStr += "\n\n\t\n"; if(dispTex.empty()) { materialStr += simpleVertTemplate; } else { materialStr += tessVertTemplate; } materialStr += "\n"; // Then fragment part materialStr += diffNormSpecFragTemplate; materialStr += "\n\t\t"; // Replace strings if(!dispTex.empty()) { materialStr = replaceAllString(materialStr, "%dispMap%", m_texrpath + dispTex); } // Diffuse if(!diffTex.empty()) { materialStr = replaceAllString(materialStr, "%diffuseColorInput%", R"(sampler2DuDiffuseColor)" + m_texrpath + diffTex + R"()"); materialStr = replaceAllString(materialStr, "%diffuseColorFunc%", readRgbFromTextureTemplate); materialStr = replaceAllString(materialStr, "%id%", "10"); materialStr = replaceAllString(materialStr, "%map%", "uDiffuseColor"); materialStr = replaceAllString(materialStr, "%diffuseColorArg%", "out10"); } else { aiColor3D diffCol = {0.0, 0.0, 0.0}; mtl.Get(AI_MATKEY_COLOR_DIFFUSE, diffCol); materialStr = replaceAllString(materialStr, "%diffuseColorInput%", R"(vec3uDiffuseColor)" + std::to_string(diffCol[0]) + " " + std::to_string(diffCol[1]) + " " + std::to_string(diffCol[2]) + R"()"); materialStr = replaceAllString(materialStr, "%diffuseColorFunc%", ""); materialStr = replaceAllString(materialStr, "%diffuseColorArg%", "uDiffuseColor"); } // Normal if(!normTex.empty()) { materialStr = replaceAllString(materialStr, "%normalInput%", R"(sampler2DuNormal)" + m_texrpath + normTex + R"()"); materialStr = replaceAllString(materialStr, "%normalFunc%", R"( 20 vec3 readNormalFromTexture out0 out1 uNormal out2 )"); materialStr = replaceAllString(materialStr, "%normalArg%", "out20"); } else { materialStr = replaceAllString(materialStr, "%normalInput%", " "); materialStr = replaceAllString(materialStr, "%normalFunc%", " "); materialStr = replaceAllString(materialStr, "%normalArg%", "out0"); } // Specular if(!specColTex.empty()) { materialStr = replaceAllString(materialStr, "%specularColorInput%", R"(sampler2DuSpecularColor)" + m_texrpath + specColTex + R"()"); materialStr = replaceAllString(materialStr, "%specularColorFunc%", readRgbFromTextureTemplate); materialStr = replaceAllString(materialStr, "%id%", "50"); materialStr = replaceAllString(materialStr, "%map%", "uSpecularColor"); materialStr = replaceAllString(materialStr, "%specularColorArg%", "out50"); } else { aiColor3D specCol = {0.0, 0.0, 0.0}; mtl.Get(AI_MATKEY_COLOR_SPECULAR, specCol); materialStr = replaceAllString(materialStr, "%specularColorInput%", R"(vec3uSpecularColor)" + std::to_string(specCol[0]) + " " + std::to_string(specCol[1]) + " " + std::to_string(specCol[2]) + R"()"); materialStr = replaceAllString(materialStr, "%specularColorFunc%", ""); materialStr = replaceAllString(materialStr, "%specularColorArg%", "uSpecularColor"); } if(!shininessTex.empty()) { materialStr = replaceAllString(materialStr, "%specularPowerInput%", R"(sampler2DuSpecularPower)" + m_texrpath + shininessTex + R"()"); materialStr = replaceAllString(materialStr, "%specularPowerValue%", m_texrpath + shininessTex); materialStr = replaceAllString(materialStr, "%specularPowerFunc%", readRFromTextureTemplate); materialStr = replaceAllString(materialStr, "%id%", "60"); materialStr = replaceAllString(materialStr, "%map%", "uSpecularPower"); materialStr = replaceAllString(materialStr, "%specularPowerArg%", "out60"); } else { float shininess = 0.0; mtl.Get(AI_MATKEY_SHININESS, shininess); const float MAX_SHININESS = 511.0; shininess = std::min(MAX_SHININESS, shininess); if(shininess > MAX_SHININESS) { LOGW("Shininness exceeds %f", MAX_SHININESS); } shininess = shininess / MAX_SHININESS; materialStr = replaceAllString(materialStr, "%specularPowerInput%", R"(floatuSpecularPower)" + std::to_string(shininess) + R"()"); materialStr = replaceAllString(materialStr, "%specularPowerFunc%", ""); materialStr = replaceAllString(materialStr, "%specularPowerArg%", "uSpecularPower"); } materialStr = replaceAllString(materialStr, "%maxSpecularPower%", " "); materialStr = replaceAllString(materialStr, "%instanced%", (instances > 1) ? "1" : "0"); materialStr = replaceAllString(materialStr, "%arraySize%", std::to_string(roundUpInstancesCount(instances))); materialStr = replaceAllString(materialStr, "%diffuseMap%", m_texrpath + diffTex); // Replace texture extensions with .anki materialStr = replaceAllString(materialStr, ".tga", ".ankitex"); materialStr = replaceAllString(materialStr, ".png", ".ankitex"); materialStr = replaceAllString(materialStr, ".jpg", ".ankitex"); materialStr = replaceAllString(materialStr, ".jpeg", ".ankitex"); // Open and write file std::fstream file; file.open(m_outputDirectory + name + ".ankimtl", std::ios::out); file << materialStr; } //============================================================================== void Exporter::exportModel(const Model& model) const { std::string name = getModelName(model); LOGI("Exporting model %s", name.c_str()); std::fstream file; file.open(m_outputDirectory + name + ".ankimdl", std::ios::out); file << XML_HEADER << '\n'; file << "\n"; file << "\t\n"; // Start patches file << "\t\t\n"; // Write mesh file << "\t\t\t" << m_rpath << getMeshName(getMeshAt(model.m_meshIndex)) << ".ankimesh\n"; // Write mesh1 if(!model.m_lod1MeshName.empty()) { bool found = false; for(unsigned i = 0; i < m_scene->mNumMeshes; i++) { if(m_scene->mMeshes[i]->mName.C_Str() == model.m_lod1MeshName) { file << "\t\t\t" << m_rpath << getMeshName(getMeshAt(i)) << ".ankimesh\n"; found = true; break; } } if(!found) { ERROR("Couldn't find the LOD1 %s", model.m_lod1MeshName.c_str()); } } // Write material const aiMaterial& mtl = *m_scene->mMaterials[model.m_materialIndex]; if(mtl.mAnKiProperties.find("material_override") == mtl.mAnKiProperties.end()) { file << "\t\t\t" << m_rpath << getMaterialName(getMaterialAt(model.m_materialIndex), model.m_instancesCount) << ".ankimtl\n"; } else { file << "\t\t\t" << mtl.mAnKiProperties.at("material_override") << "\n"; } // End patches file << "\t\t\n"; file << "\t\n"; file << "\n"; } //============================================================================== void Exporter::exportLight(const aiLight& light) { std::ofstream& file = m_sceneFile; LOGI("Exporting light %s", light.mName.C_Str()); if(light.mType != aiLightSource_POINT && light.mType != aiLightSource_SPOT) { LOGW("Skipping light %s. Unsupported type (0x%x)", light.mName.C_Str(), light.mType); return; } if(light.mAttenuationLinear != 0.0) { LOGW("Skipping light %s. Linear attenuation is not 0.0", light.mName.C_Str()); return; } file << "\nnode = scene:new" << ((light.mType == aiLightSource_POINT) ? "Point" : "Spot") << "Light(\"" << light.mName.C_Str() << "\")\n"; file << "lcomp = node:getSceneNodeBase():getLightComponent()\n"; // Colors //aiColor3D linear = computeLightColor(light.mColorDiffuse); aiVector3D linear(light.mColorDiffuse[0], light.mColorDiffuse[1], light.mColorDiffuse[2]); file << "lcomp:setDiffuseColor(Vec4.new(" << linear[0] << ", " << linear[1] << ", " << linear[2] << ", " << "1))\n"; //linear = computeLightColor(light.mColorSpecular); if(light.mProperties.find("specular_color") != light.mProperties.end()) { stringToFloatArray<3>(light.mProperties.at("specular_color"), linear); } file << "lcomp:setSpecularColor(Vec4.new(" << linear[0] << ", " << linear[1] << ", " << linear[2] << ", " << "1))\n"; // Geometry aiVector3D direction(0.0, 0.0, 1.0); switch(light.mType) { case aiLightSource_POINT: { // At this point I want the radius and have the attenuation factors // att = Ac + Al*d + Aq*d^2. When d = r then att = 0.0. Also if we // assume that Al is 0 then: // 0 = Ac + Aq*r^2. Solving by r is easy float r = sqrt(light.mAttenuationConstant / light.mAttenuationQuadratic); file << "lcomp:setRadius(" << r << ")\n"; } break; case aiLightSource_SPOT: { float dist = sqrt(light.mAttenuationConstant / light.mAttenuationQuadratic); float outer = light.mAngleOuterCone; float inner = light.mAngleInnerCone; if(outer == inner) { inner = outer / 2.0; } file << "lcomp:setInnerAngle(" << inner << ")\n" << "lcomp:setOuterAngle(" << outer << ")\n" << "lcomp:setDistance(" << dist << ")\n"; direction = light.mDirection; break; } default: assert(0); break; } // Transform const aiNode* node = findNodeWithName(light.mName.C_Str(), m_scene->mRootNode); if(node == nullptr) { ERROR("Couldn't find node for light %s", light.mName.C_Str()); } aiMatrix4x4 rot; aiMatrix4x4::RotationX(-3.1415 / 2.0, rot); writeNodeTransform("node", node->mTransformation * rot); // Extra if(light.mProperties.find("shadow") != light.mProperties.end()) { if(light.mProperties.at("shadow") == "true") { file << "lcomp:setShadowEnabled(1)\n"; } else { file << "lcomp:setShadowEnabled(0)\n"; } } if(light.mProperties.find("lens_flare") != light.mProperties.end()) { file << "node:loadLensFlare(\"" << light.mProperties.at("lens_flare") << "\")\n"; } bool lfCompRetrieved = false; if(light.mProperties.find("lens_flare_first_sprite_size") != light.mProperties.end()) { if(!lfCompRetrieved) { file << "lfcomp = node:getSceneNodeBase():" << "getLensFlareComponent()\n"; lfCompRetrieved = true; } aiVector3D vec; stringToFloatArray<2>( light.mProperties.at("lens_flare_first_sprite_size"), vec); file << "lfcomp:setFirstFlareSize(Vec2.new(" << vec[0] << ", " << vec[1] << "))\n"; } if(light.mProperties.find("lens_flare_color") != light.mProperties.end()) { if(!lfCompRetrieved) { file << "lfcomp = node:getSceneNodeBase():" << "getLensFlareComponent()\n"; lfCompRetrieved = true; } aiVector3D vec; stringToFloatArray<4>(light.mProperties.at("lens_flare_color"), vec); file << "lfcomp:setColorMultiplier(Vec4.new(" << vec[0] << ", " << vec[1] << ", " << vec[2] << ", " << vec[3] << "))\n"; } } //============================================================================== void Exporter::exportAnimation( const aiAnimation& anim, unsigned index) { // Get name std::string name = anim.mName.C_Str(); if(name.size() == 0) { name = std::string("unnamed_") + std::to_string(index); } // Find if it's skeleton animation /*bool isSkeletalAnimation = false; for(uint32_t i = 0; i < scene.mNumMeshes; i++) { const aiMesh& mesh = *scene.mMeshes[i]; if(mesh.HasBones()) { } }*/ std::fstream file; LOGI("Exporting animation %s", name.c_str()); file.open(m_outputDirectory + name + ".ankianim", std::ios::out); file << XML_HEADER << "\n"; file << "\n"; file << "\t\n"; for(uint32_t i = 0; i < anim.mNumChannels; i++) { const aiNodeAnim& nAnim = *anim.mChannels[i]; file << "\t\t\n"; // Name file << "\t\t\t" << nAnim.mNodeName.C_Str() << "\n"; // Positions file << "\t\t\t\n"; for(uint32_t j = 0; j < nAnim.mNumPositionKeys; j++) { const aiVectorKey& key = nAnim.mPositionKeys[j]; if(m_flipyz) { file << "\t\t\t\t" << "" << key.mValue[0] << " " << key.mValue[2] << " " << -key.mValue[1] << "\n"; } else { file << "\t\t\t\t" << "" << key.mValue[0] << " " << key.mValue[1] << " " << key.mValue[2] << "\n"; } } file << "\t\t\t\n"; // Rotations file << "\t\t\t\n"; for(uint32_t j = 0; j < nAnim.mNumRotationKeys; j++) { const aiQuatKey& key = nAnim.mRotationKeys[j]; aiMatrix3x3 mat = toAnkiMatrix(key.mValue.GetMatrix()); aiQuaternion quat(mat); //aiQuaternion quat(key.mValue); file << "\t\t\t\t" << "" << quat.x << " " << quat.y << " " << quat.z << " " << quat.w << "\n"; } file << "\t\t\t\n"; // Scale file << "\t\t\t\n"; for(uint32_t j = 0; j < nAnim.mNumScalingKeys; j++) { const aiVectorKey& key = nAnim.mScalingKeys[j]; // Note: only uniform scale file << "\t\t\t\t" << "" << ((key.mValue[0] + key.mValue[1] + key.mValue[2]) / 3.0) << "\n"; } file << "\t\t\t\n"; file << "\t\t\n"; } file << "\t\n"; file << "\n"; } //============================================================================== void Exporter::load() { LOGI("Loading file %s", &m_inputFilename[0]); //Assimp::DefaultLogger::create("", Logger::VERBOSE); m_importer.SetPropertyFloat(AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE, 170); const aiScene* scene = m_importer.ReadFile(m_inputFilename, 0 //| aiProcess_FindInstances | aiProcess_Triangulate | aiProcess_JoinIdenticalVertices //| aiProcess_SortByPType | aiProcess_ImproveCacheLocality | aiProcess_OptimizeMeshes | aiProcess_RemoveRedundantMaterials | aiProcess_CalcTangentSpace | aiProcess_GenSmoothNormals ); if(!scene) { ERROR("%s", m_importer.GetErrorString()); } m_scene = scene; } //============================================================================== void Exporter::visitNode(const aiNode* ainode) { if(ainode == nullptr) { return; } // For every mesh of this node for(unsigned i = 0; i < ainode->mNumMeshes; i++) { unsigned meshIndex = ainode->mMeshes[i]; unsigned mtlIndex = m_scene->mMeshes[meshIndex]->mMaterialIndex; // Check properties std::string lod1MeshName; bool special = false; for(const auto& prop : m_scene->mMeshes[meshIndex]->mProperties) { if(prop.first == "particles") { ParticleEmitter p; p.m_filename = prop.second; p.m_transform = ainode->mTransformation; m_particleEmitters.push_back(p); special = true; } else if(prop.first == "collision" && prop.second == "true") { StaticCollisionNode n; n.m_meshIndex = meshIndex; n.m_transform = ainode->mTransformation; m_staticCollisionNodes.push_back(n); special = true; } else if(prop.first == "portal" && prop.second == "true") { Portal portal; portal.m_meshIndex = meshIndex; portal.m_transform = ainode->mTransformation; m_portals.push_back(portal); special = true; } else if(prop.first == "sector" && prop.second == "true") { Sector sector; sector.m_meshIndex = meshIndex; sector.m_transform = ainode->mTransformation; m_sectors.push_back(sector); special = true; } else if(prop.first == "lod1") { lod1MeshName = prop.second; special = false; } } if(special) { continue; } // Find if there is another node with the same mesh-material-group pair std::vector::iterator it; for(it = m_nodes.begin(); it != m_nodes.end(); ++it) { const Node& node = *it; const Model& model = m_models[node.m_modelIndex]; if(model.m_meshIndex == meshIndex && model.m_materialIndex == mtlIndex && node.m_group == ainode->mGroup.C_Str() && node.m_group != "none") { break; } } if(it != m_nodes.end()) { // A node with the same model exists. It's instanced Node& node = *it; Model& model = m_models[node.m_modelIndex]; assert(node.m_transforms.size() > 0); node.m_transforms.push_back(ainode->mTransformation); ++model.m_instancesCount; break; } // Create new model Model mdl; mdl.m_meshIndex = meshIndex; mdl.m_materialIndex = mtlIndex; mdl.m_lod1MeshName = lod1MeshName; m_models.push_back(mdl); // Create new node Node node; node.m_modelIndex = m_models.size() - 1; node.m_transforms.push_back(ainode->mTransformation); node.m_group = ainode->mGroup.C_Str(); m_nodes.push_back(node); } // Go to children for(uint32_t i = 0; i < ainode->mNumChildren; i++) { visitNode(ainode->mChildren[i]); } } //============================================================================== void Exporter::exportCollisionMesh(uint32_t meshIdx) { std::string name = getMeshName(getMeshAt(meshIdx)); std::fstream file; file.open(m_outputDirectory + name + ".ankicl", std::ios::out); file << XML_HEADER << '\n'; // Write collision mesh file << "\n\tstaticMesh\n\t" << m_rpath << name << ".ankimesh\n\n"; } //============================================================================== void Exporter::exportAll() { LOGI("Exporting scene to %s", &m_outputDirectory[0]); // // Open scene file // m_sceneFile.open(m_outputDirectory + "scene.lua"); std::ofstream& file = m_sceneFile; file << "local scene = getSceneGraph()\n" << "local rot\n" << "local node\n" << "local inst\n" << "local lcomp\n"; // // Get all node/model data // visitNode(m_scene->mRootNode); // // Export collision meshes // for(auto n : m_staticCollisionNodes) { exportMesh(*m_scene->mMeshes[n.m_meshIndex], nullptr); exportCollisionMesh(n.m_meshIndex); file << "\n"; writeTransform(n.m_transform); std::string name = getMeshName(getMeshAt(n.m_meshIndex)); std::string fname = m_rpath + name + ".ankicl"; file << "node = scene:newStaticCollisionNode(\"" << name << "\", \"" << fname << "\", trf)\n"; } // // Export portals // unsigned i = 0; for(const Portal& portal : m_portals) { uint32_t meshIndex = portal.m_meshIndex; exportMesh(*m_scene->mMeshes[meshIndex], nullptr); std::string name = getMeshName(getMeshAt(meshIndex)); std::string fname = m_rpath + name + ".ankimesh"; file << "\nnode = scene:newPortal(\"" << name << i << "\", \"" << fname << "\")\n"; writeNodeTransform("node", portal.m_transform); ++i; } // // Export sectors // i = 0; for(const Sector& sector : m_sectors) { uint32_t meshIndex = sector.m_meshIndex; exportMesh(*m_scene->mMeshes[meshIndex], nullptr); std::string name = getMeshName(getMeshAt(meshIndex)); std::string fname = m_rpath + name + ".ankimesh"; file << "\nnode = scene:newSector(\"" << name << i << "\", \"" << fname << "\")\n"; writeNodeTransform("node", sector.m_transform); ++i; } // // Export particle emitters // i = 0; for(const ParticleEmitter& p : m_particleEmitters) { std::string name = "particles" + std::to_string(i); file << "\nnode = scene:newParticleEmitter(\"" << name << "\", \"" << p.m_filename << "\")\n"; writeNodeTransform("node", p.m_transform); ++i; } // // Export nodes and models. // for(uint32_t i = 0; i < m_nodes.size(); i++) { Node& node = m_nodes[i]; Model& model = m_models[node.m_modelIndex]; // TODO If not instanced bake transform exportMesh(*m_scene->mMeshes[model.m_meshIndex], nullptr); exportMaterial(*m_scene->mMaterials[model.m_materialIndex], model.m_instancesCount); exportModel(model); std::string modelName = getModelName(model); std::string nodeName = modelName + node.m_group + std::to_string(i); // Write the main node file << "\nnode = scene:newModelNode(\"" << nodeName << "\", \"" << m_rpath << modelName << ".ankimdl" << "\")\n"; writeNodeTransform("node", node.m_transforms[0]); // Write instance nodes for(unsigned j = 1; j < node.m_transforms.size(); j++) { file << "\ninst = scene:newInstanceNode(\"" << nodeName << "_inst" << (j - 1) << "\")\n" << "node:getSceneNodeBase():addChild(" << "inst:getSceneNodeBase())\n"; writeNodeTransform("inst", node.m_transforms[j]); } } // // Lights // for(unsigned i = 0; i < m_scene->mNumLights; i++) { exportLight(*m_scene->mLights[i]); } // // Animations // for(unsigned i = 0; i < m_scene->mNumAnimations; i++) { exportAnimation(*m_scene->mAnimations[i], i); } LOGI("Done exporting scene!"); }