diff --git a/code/AssetLib/FBX/FBXExporter.cpp b/code/AssetLib/FBX/FBXExporter.cpp index f7beee2e0..6bdd0b5be 100644 --- a/code/AssetLib/FBX/FBXExporter.cpp +++ b/code/AssetLib/FBX/FBXExporter.cpp @@ -2203,7 +2203,65 @@ void FBXExporter::WriteObjects () bpnode.Dump(outstream, binary, indent); }*/ - // TODO: cameras, lights + // lights + indent = 1; + lights_uids.clear(); + for (size_t li = 0; li < mScene->mNumLights; ++li) { + aiLight* l = mScene->mLights[li]; + + int64_t uid = generate_uid(); + const std::string lightNodeAttributeName = l->mName.C_Str() + FBX::SEPARATOR + "NodeAttribute"; + + FBX::Node lna("NodeAttribute"); + lna.AddProperties(uid, lightNodeAttributeName, "Light"); + FBX::Node lnap("Properties70"); + + // Light color. + lnap.AddP70colorA("Color", l->mColorDiffuse.r, l->mColorDiffuse.g, l->mColorDiffuse.b); + + // TODO Assimp light description is quite concise and do not handle light intensity. + // Default value to 1000W. + lnap.AddP70numberA("Intensity", 1000); + + // FBXLight::EType conversion + switch (l->mType) { + case aiLightSource_POINT: + lnap.AddP70enum("LightType", 0); + break; + case aiLightSource_DIRECTIONAL: + lnap.AddP70enum("LightType", 1); + break; + case aiLightSource_SPOT: + lnap.AddP70enum("LightType", 2); + lnap.AddP70numberA("InnerAngle", AI_RAD_TO_DEG(l->mAngleInnerCone)); + lnap.AddP70numberA("OuterAngle", AI_RAD_TO_DEG(l->mAngleOuterCone)); + break; + // TODO Assimp do not handle 'area' nor 'volume' lights, but FBX does. + /*case aiLightSource_AREA: + lnap.AddP70enum("LightType", 3); + lnap.AddP70enum("AreaLightShape", 0); // 0=Rectangle, 1=Sphere + break; + case aiLightSource_VOLUME: + lnap.AddP70enum("LightType", 4); + break;*/ + default: + break; + } + + // Did not understood how to configure the decay so disabling attenuation. + lnap.AddP70enum("DecayType", 0); + + // Dump to FBX stream + lna.AddChild(lnap); + lna.AddChild("TypeFlags", FBX::FBXExportProperty("Light")); + lna.AddChild("GeometryVersion", FBX::FBXExportProperty(int32_t(124))); + lna.Dump(outstream, binary, indent); + + // Store name and uid (will be used later when parsing scene nodes) + lights_uids[l->mName.C_Str()] = uid; + } + + // TODO: cameras // write nodes (i.e. model hierarchy) // start at root node @@ -2607,10 +2665,19 @@ void FBXExporter::WriteModelNodes( // and connect them connections.emplace_back("C", "OO", node_attribute_uid, node_uid); } else { - // generate a null node so we can add children to it - WriteModelNode( - outstream, binary, node, node_uid, "Null", transform_chain - ); + const auto& lightIt = lights_uids.find(node->mName.C_Str()); + if(lightIt != lights_uids.end()) { + // Node has a light connected to it. + WriteModelNode( + outstream, binary, node, node_uid, "Light", transform_chain + ); + connections.emplace_back("C", "OO", lightIt->second, node_uid); + } else { + // generate a null node so we can add children to it + WriteModelNode( + outstream, binary, node, node_uid, "Null", transform_chain + ); + } } // if more than one child mesh, make nodes for each mesh diff --git a/code/AssetLib/FBX/FBXExporter.h b/code/AssetLib/FBX/FBXExporter.h index dcd1d2727..563183268 100644 --- a/code/AssetLib/FBX/FBXExporter.h +++ b/code/AssetLib/FBX/FBXExporter.h @@ -63,6 +63,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. struct aiScene; struct aiNode; +struct aiLight; //struct aiMaterial; namespace Assimp @@ -95,6 +96,7 @@ namespace Assimp std::vector mesh_uids; std::vector material_uids; std::map node_uids; + std::map lights_uids; // this crude unique-ID system is actually fine int64_t last_uid = 999999; diff --git a/code/Common/SceneCombiner.cpp b/code/Common/SceneCombiner.cpp index 555d46b6a..8f10d6308 100644 --- a/code/Common/SceneCombiner.cpp +++ b/code/Common/SceneCombiner.cpp @@ -406,11 +406,25 @@ void SceneCombiner::MergeScenes(aiScene **_dest, aiScene *master, std::vector, // where n is the index of the texture. - aiString &s = *((aiString *)prop->mData); + // Copy here because we overwrite the string data in-place and the buffer inside of aiString + // will be a lie if we just reinterpret from prop->mData. The size of mData is not guaranteed to be + // MAXLEN in size. + aiString s(*(aiString *)prop->mData); if ('*' == s.data[0]) { // Offset the index and write it back .. const unsigned int idx = strtoul10(&s.data[1]) + offset[n]; - ASSIMP_itoa10(&s.data[1], sizeof(s.data) - 1, idx); + const unsigned int oldLen = s.length; + + s.length = 1 + ASSIMP_itoa10(&s.data[1], sizeof(s.data) - 1, idx); + + // The string changed in size so we need to reallocate the buffer for the property. + if (oldLen < s.length) { + prop->mDataLength += s.length - oldLen; + delete[] prop->mData; + prop->mData = new char[prop->mDataLength]; + } + + memcpy(prop->mData, static_cast(&s), prop->mDataLength); } }