| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202 |
- // Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
- // All rights reserved.
- // Code licensed under the BSD License.
- // http://www.anki3d.org/LICENSE
- #include "Exporter.h"
- #include <iostream>
- static const char* XML_HEADER = R"(<?xml version="1.0" encoding="UTF-8" ?>)";
- 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;
- }
- 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, unsigned* depth = nullptr)
- {
- if(node == nullptr || node->mName.C_Str() == name)
- {
- return node;
- }
- if(depth)
- {
- ++(*depth);
- }
- const aiNode* out = nullptr;
- // Go to children
- for(unsigned i = 0; i < node->mNumChildren; i++)
- {
- out = findNodeWithName(name, node->mChildren[i]);
- if(out)
- {
- break;
- }
- }
- return out;
- }
- static std::vector<std::string> tokenize(const std::string& source)
- {
- const char* delimiter = " ";
- bool keepEmpty = false;
- std::vector<std::string> 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<int N, typename Arr>
- static void stringToFloatArray(const std::string& in, Arr& out)
- {
- std::vector<std::string> 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;
- }
- }
- static void removeScale(aiMatrix4x4& m)
- {
- aiVector3D xAxis(m.a1, m.b1, m.c1);
- aiVector3D yAxis(m.a2, m.b2, m.c2);
- aiVector3D zAxis(m.a3, m.b3, m.c3);
- float scale = xAxis.Length();
- m.a1 /= scale;
- m.b1 /= scale;
- m.c1 /= scale;
- scale = yAxis.Length();
- m.a2 /= scale;
- m.b2 /= scale;
- m.c2 /= scale;
- scale = zAxis.Length();
- m.a3 /= scale;
- m.b3 /= scale;
- m.c3 /= scale;
- }
- static float getUniformScale(const aiMatrix4x4& m)
- {
- const float SCALE_THRESHOLD = 0.01; // 1 cm
- aiVector3D xAxis(m.a1, m.b1, m.c1);
- aiVector3D yAxis(m.a2, m.b2, m.c2);
- aiVector3D zAxis(m.a3, m.b3, m.c3);
- float scale = xAxis.Length();
- if(std::abs(scale - yAxis.Length()) > SCALE_THRESHOLD || std::abs(scale - zAxis.Length()) > SCALE_THRESHOLD)
- {
- ERROR("No uniform scale in the matrix");
- }
- return scale;
- }
- static aiVector3D getNonUniformScale(const aiMatrix4x4& m)
- {
- aiVector3D xAxis(m.a1, m.b1, m.c1);
- aiVector3D yAxis(m.a2, m.b2, m.c2);
- aiVector3D zAxis(m.a3, m.b3, m.c3);
- aiVector3D scale;
- scale[0] = xAxis.Length();
- scale[1] = yAxis.Length();
- scale[2] = zAxis.Length();
- return scale;
- }
- std::string Exporter::getMaterialName(const aiMaterial& mtl)
- {
- aiString ainame;
- std::string name;
- if(mtl.Get(AI_MATKEY_NAME, ainame) == AI_SUCCESS)
- {
- name = ainame.C_Str();
- }
- else
- {
- ERROR("Material's name is missing");
- }
- return name;
- }
- 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& inmat)
- {
- aiMatrix4x4 mat = inmat;
- std::ofstream& file = m_sceneFile;
- float pos[3];
- pos[0] = mat[0][3];
- pos[1] = mat[1][3];
- pos[2] = mat[2][3];
- file << "trf = Transform.new()\n";
- file << "trf:setOrigin(Vec4.new(" << pos[0] << ", " << pos[1] << ", " << pos[2] << ", 0))\n";
- float scale = getUniformScale(mat);
- removeScale(mat);
- 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 << mat[j][i];
- }
- if(!(i == 3 && j == 2))
- {
- file << ", ";
- }
- }
- }
- file << ")\n";
- file << "trf:setRotation(rot)\n";
- file << "trf:setScale(" << scale << ")\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));
- 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());
- // Find the root bone
- unsigned minDepth = 0xFFFFFFFF;
- std::string rootBoneName;
- for(uint32_t i = 0; i < mesh.mNumBones; i++)
- {
- const aiBone& bone = *mesh.mBones[i];
- unsigned depth = 0;
- const aiNode* node = findNodeWithName(bone.mName.C_Str(), m_scene->mRootNode, &depth);
- if(!node)
- {
- ERROR("Bone \"%s\" was not found in the scene hierarchy", bone.mName.C_Str());
- }
- if(depth < minDepth)
- {
- minDepth = depth;
- rootBoneName = bone.mName.C_Str();
- }
- }
- assert(!rootBoneName.empty());
- // Open file
- file.open(m_outputDirectory + name + ".ankiskel", std::ios::out);
- file << XML_HEADER << "\n";
- file << "<skeleton>\n";
- file << "\t<bones>\n";
- for(uint32_t i = 0; i < mesh.mNumBones; i++)
- {
- const aiBone& bone = *mesh.mBones[i];
- file << "\t\t<bone>\n";
- // <name>
- file << "\t\t\t<name>" << bone.mName.C_Str() << "</name>\n";
- // <bontTransform>
- aiMatrix4x4 akMat = toAnkiMatrix(bone.mOffsetMatrix);
- file << "\t\t\t<boneTransform>";
- for(unsigned j = 0; j < 4; j++)
- {
- for(unsigned i = 0; i < 4; i++)
- {
- file << akMat[j][i] << " ";
- }
- }
- file << "</boneTransform>\n";
- // <transform>
- const aiNode* node = findNodeWithName(bone.mName.C_Str(), m_scene->mRootNode);
- assert(node);
- akMat = toAnkiMatrix(node->mTransformation);
- file << "\t\t\t<transform>";
- for(unsigned j = 0; j < 4; j++)
- {
- for(unsigned i = 0; i < 4; i++)
- {
- file << akMat[j][i] << " ";
- }
- }
- file << "</transform>\n";
- // <parent>
- if(bone.mName.C_Str() != rootBoneName)
- {
- file << "\t\t\t<parent>" << node->mParent->mName.C_Str() << "</parent>\n";
- }
- file << "\t\t</bone>\n";
- }
- file << "\t</bones>\n";
- file << "</skeleton>\n";
- }
- 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 << "<model>\n";
- file << "\t<modelPatches>\n";
- // Start patches
- file << "\t\t<modelPatch>\n";
- // Write mesh
- file << "\t\t\t<mesh>" << m_rpath << getMeshName(getMeshAt(model.m_meshIndex)) << ".ankimesh</mesh>\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<mesh1>" << m_rpath << getMeshName(getMeshAt(i)) << ".ankimesh</mesh1>\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<material>" << m_rpath << getMaterialName(getMaterialAt(model.m_materialIndex))
- << ".ankimtl</material>\n";
- }
- else
- {
- file << "\t\t\t<material>" << mtl.mAnKiProperties.at("material_override") << "</material>\n";
- }
- // End patches
- file << "\t\t</modelPatch>\n";
- file << "\t</modelPatches>\n";
- // Skeleton
- const aiMesh& aimesh = *m_scene->mMeshes[model.m_meshIndex];
- if(aimesh.HasBones())
- {
- exportSkeleton(aimesh);
- file << "\t<skeleton>" << m_rpath << aimesh.mName.C_Str() << ".ankiskel</skeleton>\n";
- }
- file << "</model>\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") << "LightNode(\""
- << 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", toAnkiMatrix(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";
- }
- bool eventCreated = false;
- if(light.mProperties.find("light_event_intensity") != light.mProperties.end())
- {
- if(!eventCreated)
- {
- file << "event = events:newLightEvent(0.0, -1.0, node:getSceneNodeBase())\n";
- eventCreated = true;
- }
- aiVector3D vec;
- stringToFloatArray<4>(light.mProperties.at("light_event_intensity"), vec);
- file << "event:setIntensityMultiplier(Vec4.new(" << vec[0] << ", " << vec[1] << ", " << vec[2] << ", " << vec[3]
- << "))\n";
- }
- if(light.mProperties.find("light_event_frequency") != light.mProperties.end())
- {
- if(!eventCreated)
- {
- file << "event = events:newLightEvent(0.0, -1.0, node:getSceneNodeBase())\n";
- eventCreated = true;
- }
- float vec[2];
- stringToFloatArray<2>(light.mProperties.at("light_event_frequency"), vec);
- file << "event:setFrequency(" << vec[0] << ", " << vec[1] << ")\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 << "<animation>\n";
- file << "\t<channels>\n";
- for(uint32_t i = 0; i < anim.mNumChannels; i++)
- {
- const aiNodeAnim& nAnim = *anim.mChannels[i];
- file << "\t\t<channel>\n";
- // Name
- file << "\t\t\t<name>" << nAnim.mNodeName.C_Str() << "</name>\n";
- // Positions
- file << "\t\t\t<positionKeys>\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><time>" << key.mTime << "</time><value>" << key.mValue[0] << " " << key.mValue[2]
- << " " << -key.mValue[1] << "</value></key>\n";
- }
- else
- {
- file << "\t\t\t\t<key><time>" << key.mTime << "</time><value>" << key.mValue[0] << " " << key.mValue[1]
- << " " << key.mValue[2] << "</value></key>\n";
- }
- }
- file << "\t\t\t</positionKeys>\n";
- // Rotations
- file << "\t\t\t<rotationKeys>\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<key><time>" << key.mTime << "</time>"
- << "<value>" << quat.x << " " << quat.y << " " << quat.z << " " << quat.w << "</value></key>\n";
- }
- file << "\t\t\t</rotationKeys>\n";
- // Scale
- file << "\t\t\t<scalingKeys>\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><time>" << key.mTime << "</time>"
- << "<value>" << ((key.mValue[0] + key.mValue[1] + key.mValue[2]) / 3.0) << "</value></key>\n";
- }
- file << "\t\t\t</scalingKeys>\n";
- file << "\t\t</channel>\n";
- }
- file << "\t</channels>\n";
- file << "</animation>\n";
- }
- void Exporter::exportCamera(const aiCamera& cam)
- {
- std::ofstream& file = m_sceneFile;
- LOGI("Exporting camera %s", cam.mName.C_Str());
- // Write the main node
- file << "\nnode = scene:newPerspectiveCameraNode(\"" << cam.mName.C_Str() << "\")\n";
- file << "scene:setActiveCameraNode(node:getSceneNodeBase())\n";
- file << "node:setAll(" << cam.mHorizontalFOV << ", "
- << "1.0 / getMainRenderer():getAspectRatio() * " << cam.mHorizontalFOV << ", " << cam.mClipPlaneNear << ", "
- << cam.mClipPlaneFar << ")\n";
- // Find the node
- const aiNode* node = findNodeWithName(cam.mName.C_Str(), m_scene->mRootNode);
- if(node == nullptr)
- {
- ERROR("Couldn't find node for camera %s", cam.mName.C_Str());
- }
- aiMatrix4x4 rot;
- aiMatrix4x4::RotationX(-3.1415 / 2.0, rot);
- writeNodeTransform("node", toAnkiMatrix(node->mTransformation * rot));
- }
- void Exporter::load()
- {
- LOGI("Loading file %s", &m_inputFilename[0]);
- const int smoothAngle = 170;
- m_importer.SetPropertyFloat(AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE, smoothAngle);
- unsigned flags = 0
- //| aiProcess_FindInstances
- | aiProcess_JoinIdenticalVertices
- //| aiProcess_SortByPType
- | aiProcess_ImproveCacheLocality | aiProcess_OptimizeMeshes | aiProcess_RemoveRedundantMaterials
- | aiProcess_CalcTangentSpace | aiProcess_GenSmoothNormals;
- const aiScene* scene = m_importer.ReadFile(m_inputFilename, flags | aiProcess_Triangulate);
- if(!scene)
- {
- ERROR("%s", m_importer.GetErrorString());
- }
- m_scene = scene;
- // Load without triangulation
- m_importerNoTriangles.SetPropertyFloat(AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE, smoothAngle);
- scene = m_importerNoTriangles.ReadFile(m_inputFilename, flags);
- if(!scene)
- {
- ERROR("%s", m_importerNoTriangles.GetErrorString());
- }
- m_sceneNoTriangles = 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;
- std::string collisionMesh;
- 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 = toAnkiMatrix(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 = toAnkiMatrix(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 = toAnkiMatrix(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 = toAnkiMatrix(ainode->mTransformation);
- m_sectors.push_back(sector);
- special = true;
- }
- else if(prop.first == "lod1")
- {
- lod1MeshName = prop.second;
- special = false;
- }
- else if(prop.first == "reflection_probe" && prop.second == "true")
- {
- ReflectionProbe probe;
- aiMatrix4x4 trf = toAnkiMatrix(ainode->mTransformation);
- probe.m_position = aiVector3D(trf.a4, trf.b4, trf.c4);
- aiVector3D zAxis(trf.a3, trf.b3, trf.c3);
- float scale = zAxis.Length();
- probe.m_radius = scale;
- m_reflectionProbes.push_back(probe);
- special = true;
- }
- else if(prop.first == "reflection_proxy" && prop.second == "true")
- {
- ReflectionProxy proxy;
- // Find proxy in the other scene
- proxy.m_meshIndex = 0xFFFFFFFF;
- for(unsigned i = 0; i < m_sceneNoTriangles->mNumMeshes; ++i)
- {
- if(m_sceneNoTriangles->mMeshes[i]->mName == m_scene->mMeshes[meshIndex]->mName)
- {
- // Found
- proxy.m_meshIndex = i;
- break;
- }
- }
- if(proxy.m_meshIndex == 0xFFFFFFFF)
- {
- ERROR("Reflection proxy mesh not found");
- }
- proxy.m_transform = toAnkiMatrix(ainode->mTransformation);
- m_reflectionProxies.push_back(proxy);
- special = true;
- }
- else if(prop.first == "occluder" && prop.second == "true")
- {
- OccluderNode occluder;
- occluder.m_meshIndex = meshIndex;
- occluder.m_transform = toAnkiMatrix(ainode->mTransformation);
- m_occluders.push_back(occluder);
- special = true;
- }
- else if(prop.first == "collision_mesh")
- {
- collisionMesh = prop.second;
- special = false;
- }
- else if(prop.first.find("decal_") == 0)
- {
- DecalNode decal;
- for(const auto& pr : m_scene->mMeshes[meshIndex]->mProperties)
- {
- if(pr.first == "decal_diffuse_atlas")
- {
- decal.m_diffuseTextureAtlasFilename = pr.second;
- }
- else if(pr.first == "decal_diffuse_sub_texture")
- {
- decal.m_diffuseSubTextureName = pr.second;
- }
- else if(pr.first == "decal_diffuse_factor")
- {
- decal.m_factors[0] = std::stof(pr.second);
- }
- else if(pr.first == "decal_normal_roughness_atlas")
- {
- decal.m_specularRoughnessAtlasFilename = pr.second;
- }
- else if(pr.first == "decal_normal_roughness_sub_texture")
- {
- decal.m_specularRoughnessSubTextureName = pr.second;
- }
- else if(pr.first == "decal_normal_roughness_factor")
- {
- decal.m_factors[1] = std::stof(pr.second);
- }
- }
- if(decal.m_diffuseTextureAtlasFilename.empty() || decal.m_diffuseSubTextureName.empty())
- {
- ERROR("Missing decal information");
- }
- aiMatrix4x4 trf = toAnkiMatrix(ainode->mTransformation);
- decal.m_size = getNonUniformScale(trf);
- removeScale(trf);
- decal.m_transform = trf;
- m_decals.push_back(decal);
- special = true;
- break;
- }
- }
- if(special)
- {
- continue;
- }
- // 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_transform = toAnkiMatrix(ainode->mTransformation);
- node.m_group = ainode->mGroup.C_Str();
- node.m_collisionMesh = collisionMesh;
- 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 << "<collisionShape>\n\t<type>staticMesh</type>\n\t<value>" << m_rpath << name
- << ".ankimesh</value>\n</collisionShape>\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 events = getEventManager()\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, 3);
- 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, 3);
- std::string name = getMeshName(getMeshAt(meshIndex));
- std::string fname = m_rpath + name + ".ankimesh";
- file << "\nnode = scene:newPortalNode(\"" << 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, 3);
- std::string name = getMeshName(getMeshAt(meshIndex));
- std::string fname = m_rpath + name + ".ankimesh";
- file << "\nnode = scene:newSectorNode(\"" << 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:newParticleEmitterNode(\"" << name << "\", \"" << p.m_filename << "\")\n";
- writeNodeTransform("node", p.m_transform);
- ++i;
- }
- //
- // Export probes
- //
- i = 0;
- for(const ReflectionProbe& probe : m_reflectionProbes)
- {
- std::string name = "reflprobe" + std::to_string(i);
- file << "\nnode = scene:newReflectionProbeNode(\"" << name << "\", " << probe.m_radius << ")\n";
- aiMatrix4x4 trf;
- aiMatrix4x4::Translation(probe.m_position, trf);
- writeNodeTransform("node", trf);
- ++i;
- }
- //
- // Export proxies
- //
- i = 0;
- for(const ReflectionProxy& proxy : m_reflectionProxies)
- {
- const aiMesh& mesh = *m_sceneNoTriangles->mMeshes[proxy.m_meshIndex];
- exportMesh(mesh, nullptr, 4);
- std::string name = "reflproxy" + std::to_string(i);
- file << "\nnode = scene:newReflectionProxyNode(\"" << name << "\", \"" << m_rpath << mesh.mName.C_Str()
- << ".ankimesh\")\n";
- writeNodeTransform("node", proxy.m_transform);
- ++i;
- }
- //
- // Export occluders
- //
- i = 0;
- for(const OccluderNode& occluder : m_occluders)
- {
- const aiMesh& mesh = *m_scene->mMeshes[occluder.m_meshIndex];
- exportMesh(mesh, nullptr, 3);
- std::string name = "occluder" + std::to_string(i);
- file << "\nnode = scene:newOccluderNode(\"" << name << "\", \"" << m_rpath << mesh.mName.C_Str()
- << ".ankimesh\")\n";
- writeNodeTransform("node", occluder.m_transform);
- ++i;
- }
- //
- // Export decals
- //
- i = 0;
- for(const DecalNode& decal : m_decals)
- {
- std::string name = "decal" + std::to_string(i);
- file << "\nnode = scene:newDecalNode(\"" << name << "\")\n";
- writeNodeTransform("node", decal.m_transform);
- file << "decalc = node:getSceneNodeBase():getDecalComponent()\n";
- file << "decalc:setDiffuseDecal(\"" << decal.m_diffuseTextureAtlasFilename << "\", \""
- << decal.m_diffuseSubTextureName << "\", " << decal.m_factors[0] << ")\n";
- file << "decalc:updateShape(" << decal.m_size.x << ", " << decal.m_size.y << ", " << decal.m_size.z << ")\n";
- if(!decal.m_specularRoughnessAtlasFilename.empty())
- {
- file << "decalc:setSpecularRoughnessDecal(\"" << decal.m_specularRoughnessAtlasFilename << "\", \""
- << decal.m_specularRoughnessSubTextureName << "\", " << decal.m_factors[1] << ")\n";
- }
- ++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 static bake transform
- exportMesh(*m_scene->mMeshes[model.m_meshIndex], nullptr, 3);
- exportMaterial(*m_scene->mMaterials[model.m_materialIndex]);
- 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_transform);
- // Write the collision node
- if(!node.m_collisionMesh.empty())
- {
- bool found = false;
- unsigned i = 0;
- for(; i < m_scene->mNumMeshes; i++)
- {
- if(m_scene->mMeshes[i]->mName.C_Str() == node.m_collisionMesh)
- {
- found = true;
- break;
- }
- }
- if(found)
- {
- exportCollisionMesh(i);
- std::string fname = m_rpath + node.m_collisionMesh + ".ankicl";
- file << "node = scene:newStaticCollisionNode(\"" << nodeName << "_cl\", \"" << fname << "\", trf)\n";
- }
- else
- {
- ERROR("Couldn't find the collision_mesh %s", node.m_collisionMesh.c_str());
- }
- }
- }
- //
- // 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);
- }
- //
- // Cameras
- //
- for(unsigned i = 0; i < m_scene->mNumCameras; i++)
- {
- exportCamera(*m_scene->mCameras[i]);
- }
- LOGI("Done exporting scene!");
- }
|