/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. #include "StdAfx.h" #include "ColladaWriter.h" #include "IExportSource.h" #include "ISettings.h" #include "XMLWriter.h" #include "SkeletonData.h" #include "AnimationData.h" #include "ProgressRange.h" #include "ModelData.h" #include "IExportContext.h" #include "GeometryFileData.h" #include "GeometryData.h" #include "MaterialData.h" #include "SkinningData.h" #include "Cry_Math.h" #include "LocaleChanger.h" #include "MorphData.h" #include "StringHelpers.h" #include "GeometryMaterialData.h" #include "ColladaShared.h" #include #include #include #include namespace { bool FloatingPointHasPrecisionIssues() { Matrix44 m; m.m00 = 0.729367f; m.m01 = -0.143863f; m.m02 = -0.668825f; m.m03 = 0.595435f; m.m10 = -0.573746f; m.m11 = 0.403844f; m.m12 = -0.712549f; m.m13 = 1.14523f; m.m20 = 0.37261f; m.m21 = 0.903445f; m.m22 = 0.21201f; m.m23 = 0.0669039f; m.m30 = 0; m.m31 = 0; m.m32 = 0; m.m33 = 1; m.Invert(); return m.m33 <= 0.999f || m.m33 >= 1.001f; // For testing with www.wolframalpha.com: // string a = StringHelpers::Format("inverse{{%g,%g,%g,%g},{%g,%g,%g,%g},{%g,%g,%g,%g},{%g,%g,%g,%g}}", // m.m00, m.m01, m.m02, m.m03, // m.m10, m.m11, m.m12, m.m13, // m.m20, m.m21, m.m22, m.m23, // m.m30, m.m31, m.m32, m.m33); } } namespace { void DecomposeTransform(Vec3& translation, CryQuat& rotation, Vec3& scale, const Matrix34& transform) { translation = transform.GetTranslation(); Matrix33 orientation(transform); scale.x = Vec3(orientation.m00, orientation.m10, orientation.m20).GetLength(); scale.y = Vec3(orientation.m01, orientation.m11, orientation.m21).GetLength(); scale.z = Vec3(orientation.m02, orientation.m12, orientation.m22).GetLength(); orientation.OrthonormalizeFast(); rotation = !CryQuat(orientation); } struct BoneEntry { std::string name; std::string physName; std::string parentFrameName; }; typedef std::map, SkeletonData> SkeletonDataMap; typedef std::map, MorphData> MorphDataMap; typedef std::map, std::vector > BoneDataMap; struct GeometryEntry { GeometryEntry(const std::string& name, int geometryFileIndex, int modelIndex) : name(name) , geometryFileIndex(geometryFileIndex) , modelIndex(modelIndex) {} std::string name; int geometryFileIndex; int modelIndex; }; struct BoneGeometryEntry { BoneGeometryEntry(const std::string& name, int geometryFileIndex, int modelIndex, int boneIndex) : name(name) , geometryFileIndex(geometryFileIndex) , modelIndex(modelIndex) , boneIndex(boneIndex) {} std::string name; int geometryFileIndex; int modelIndex; int boneIndex; }; struct MorphGeometryEntry { MorphGeometryEntry(const std::string& name, const std::string& morphName, int geometryFileIndex, int modelIndex, int morphIndex) : name(name) , morphName(morphName) , geometryFileIndex(geometryFileIndex) , modelIndex(modelIndex) , morphIndex(morphIndex) {} std::string name; std::string morphName; int geometryFileIndex; int modelIndex; int morphIndex; }; struct EffectsEntry { EffectsEntry(const std::string& name) : name(name) {} std::string name; }; struct MaterialEntry { MaterialEntry(const std::string& name) : name(name) {} std::string name; }; struct SkinControllerEntry { std::string name; int geometryFileIndex; int modelIndex; }; struct MorphControllerEntry { std::string name; int geometryFileIndex; int modelIndex; }; typedef std::map, MorphControllerEntry> MorphControllerMap; void BindMaterials(XMLWriter& writer, IExportContext* context, MaterialData& materialData, const ModelData& modelData, int modelIndex, const std::map& materialMaterialMap, const std::vector& materials, IExportSource* source) { // Instance any materials that the node uses. GeometryMaterialData geometryMaterialData; source->ReadGeometryMaterialData(context, &geometryMaterialData, &modelData, &materialData, modelIndex); std::set usedMaterialIndices; for (int materialIndex = 0, materialCount = geometryMaterialData.GetUsedMaterialCount(); materialIndex < materialCount; ++materialIndex) { usedMaterialIndices.insert(geometryMaterialData.GetUsedMaterialIndex(materialIndex)); } if (!usedMaterialIndices.empty()) { XMLWriter::Element bindMaterialElement(writer, "bind_material"); XMLWriter::Element techniqueCommonElement(writer, "technique_common"); for (std::set::const_iterator usedMtlPos = usedMaterialIndices.begin(), usedMtlEnd = usedMaterialIndices.end(); usedMtlPos != usedMtlEnd; ++usedMtlPos) { std::map::const_iterator entryMapPos = materialMaterialMap.find(*usedMtlPos); int entryIndex = (entryMapPos != materialMaterialMap.end() ? (*entryMapPos).second : -1); std::string name = (entryIndex >= 0 ? materials[entryIndex].name : "UNKNOWN_INSTANCED_MATERIAL"); XMLWriter::Element instanceMaterialElement(writer, "instance_material"); instanceMaterialElement.Attribute("symbol", name); instanceMaterialElement.Attribute("target", "#" + name); } } } void BindBoneMaterials(XMLWriter& writer, IExportContext* context, MaterialData& materialData, SkeletonData& skeletonData, int boneIndex, const std::map& materialMaterialMap, const std::vector& materials, IExportSource* source) { // Instance any materials that the node uses. GeometryMaterialData geometryMaterialData; source->ReadBoneGeometryMaterialData(context, &geometryMaterialData, &skeletonData, boneIndex, &materialData); std::set usedMaterialIndices; for (int materialIndex = 0, materialCount = geometryMaterialData.GetUsedMaterialCount(); materialIndex < materialCount; ++materialIndex) { usedMaterialIndices.insert(geometryMaterialData.GetUsedMaterialIndex(materialIndex)); } if (!usedMaterialIndices.empty()) { XMLWriter::Element bindMaterialElement(writer, "bind_material"); XMLWriter::Element techniqueCommonElement(writer, "technique_common"); for (std::set::const_iterator usedMtlPos = usedMaterialIndices.begin(), usedMtlEnd = usedMaterialIndices.end(); usedMtlPos != usedMtlEnd; ++usedMtlPos) { std::map::const_iterator entryMapPos = materialMaterialMap.find(*usedMtlPos); int entryIndex = (entryMapPos != materialMaterialMap.end() ? (*entryMapPos).second : -1); std::string name = (entryIndex >= 0 ? materials[entryIndex].name : "UNKNOWN_INSTANCED_MATERIAL"); XMLWriter::Element instanceMaterialElement(writer, "instance_material"); instanceMaterialElement.Attribute("symbol", name); instanceMaterialElement.Attribute("target", "#" + name); } } //// Instance any materials that the node uses. //std::vector materialIDs; //for (int materialIndex = 0, materialCount = materialData.GetMaterialCount(); materialIndex < materialCount; ++materialIndex) //{ // int parentIndex = materialData.GetParentIndex(materialIndex); // if (parentIndex >= 0 && parentIndex == skeletonData.GetMaterial(boneIndex)) // materialIDs.push_back(materialIndex); //} //if (!materialIDs.empty()) //{ // XMLWriter::Element bindMaterialElement(writer, "bind_material"); // XMLWriter::Element techniqueCommonElement(writer, "technique_common"); // for (int i = 0, count = int(materialIDs.size()); i < count; ++i) // { // std::map::const_iterator entryMapPos = materialMaterialMap.find(materialIDs[i]); // int entryIndex = (entryMapPos != materialMaterialMap.end() ? (*entryMapPos).second : -1); // std::string name = (entryIndex >= 0 ? materials[entryIndex].name : 0); // XMLWriter::Element instanceMaterialElement(writer, "instance_material"); // instanceMaterialElement.Attribute("symbol", name); // instanceMaterialElement.Attribute("target", "#" + name); // } //} } void WriteExtraData(XMLWriter& writer, const SHelperData& helperData, const std::string& properties) { // Write helper data (if it's helper node) and properties if ((!properties.empty()) || (helperData.m_eHelperType != SHelperData::eHelperType_UNKNOWN)) { XMLWriter::Element elem(writer, "extra"); { XMLWriter::Element elem(writer, "technique"); elem.Attribute("profile", "CryEngine"); { if (!properties.empty()) { // TODO: Sokov: check for invalid characters in properties string, like '<', '>', <' ', >127 etc. XMLWriter::Element elem(writer, "properties"); elem.Content(properties); } if (helperData.m_eHelperType != SHelperData::eHelperType_UNKNOWN) { XMLWriter::Element elem(writer, "helper"); switch (helperData.m_eHelperType) { case SHelperData::eHelperType_Point: elem.Attribute("type", "point"); break; case SHelperData::eHelperType_Dummy: elem.Attribute("type", "dummy"); { XMLWriter::Element elem(writer, "bound_box_min"); elem.ContentArrayElement(helperData.m_boundBoxMin[0]); elem.ContentArrayElement(helperData.m_boundBoxMin[1]); elem.ContentArrayElement(helperData.m_boundBoxMin[2]); } { XMLWriter::Element elem(writer, "bound_box_max"); elem.ContentArrayElement(helperData.m_boundBoxMax[0]); elem.ContentArrayElement(helperData.m_boundBoxMax[1]); elem.ContentArrayElement(helperData.m_boundBoxMax[2]); } break; default: assert(false); elem.Attribute("type", "UNKNOWN"); break; } } } } } // ****** I dont think we need this anymore. These files are not readable by XSI as far as I know. ****** // ****** Removed 03-Jan-2012 // Write special properties for importing to the XSI. /*{ XMLWriter::Element elem(writer, "technique"); elem.Attribute("profile","XSI"); { XMLWriter::Element elem(writer, "XSI_CustomPSet"); elem.Attribute("name","ObjectProperties"); { XMLWriter::Element elem(writer, "propagation"); elem.Content("NODE"); } { XMLWriter::Element elem(writer, "type"); elem.Content("CryNodeProperties"); } { XMLWriter::Element elem(writer, "XSI_Parameter"); elem.Attribute("id","Props"); elem.Attribute("type","Text"); elem.Attribute("value",properties.c_str()); } } }*/ } void WriteSkeletonRecurse(XMLWriter& writer, IExportContext* context, const std::string& modelName, SkeletonData& skeletonData, int boneIndex, const std::string& name, const std::vector& bones, std::map, int>, int>& boneGeometryMap, std::vector& boneGeometries, int geometryFileIndex, int modelIndex, MaterialData& materialData, const std::map& materialMaterialMap, const std::vector& materials, IExportSource* source, ProgressRange& progressRange) { XMLWriter::Element nodeElement(writer, "node"); nodeElement.Attribute("id", name); // The ID must be unique. nodeElement.Attribute("name", name); // The name must not include model name as prefix, so it can match the skeleton. // Calculate the transforms for the bone and its parent. This could be made a lot simpler by using proper // transforms in the skeleton data. Matrix34 transform; { Matrix44 transforms[2]; int boneIndices[2] = {boneIndex, skeletonData.GetBoneParentIndex(boneIndex)}; for (int i = 0; i < 2; ++i) { transforms[i] = IDENTITY; if (boneIndices[i] >= 0) { Vec3 scaleParams; skeletonData.GetScale((float*)&scaleParams, boneIndices[i]); Matrix44 scale = Matrix33::CreateScale(scaleParams); Ang3 rotationParams; skeletonData.GetRotation((float*)&rotationParams, boneIndices[i]); Matrix44 rotation = Matrix33::CreateRotationXYZ(rotationParams); Vec3 translationParams; skeletonData.GetTranslation((float*)&translationParams, boneIndices[i]); Matrix44 translation(IDENTITY); translation.SetTranslation(translationParams); transforms[i] = translation * (rotation * scale); } } transform = Matrix34(transforms[1].GetInverted() * transforms[0]); } Vec3 translation, scaling; CryQuat orientation; DecomposeTransform(translation, orientation, scaling, transform); Ang3 rotation = Ang3::GetAnglesXYZ(orientation); // Write translation element. { XMLWriter::Element translateElement(writer, "translate"); translateElement.Attribute("sid", "translation"); translateElement.ContentArrayElement(translation[0]); translateElement.ContentArrayElement(translation[1]); translateElement.ContentArrayElement(translation[2]); } // Write rotation elements. for (int axisIndex = 0; axisIndex < 3; ++axisIndex) { XMLWriter::Element rotateElement(writer, "rotate"); char sidBuffer[1024]; sprintf(sidBuffer, "rotation_%c", 'z' - axisIndex); rotateElement.Attribute("sid", sidBuffer); rotateElement.ContentArrayElement(axisIndex == 2 ? 1.0f : 0.0f); rotateElement.ContentArrayElement(axisIndex == 1 ? 1.0f : 0.0f); rotateElement.ContentArrayElement(axisIndex == 0 ? 1.0f : 0.0f); rotateElement.ContentArrayElement(rotation[2 - axisIndex] * 180.0f / 3.14159f); } // Write scale elements { XMLWriter::Element scaleElement(writer, "scale"); scaleElement.Attribute("sid", "scale"); scaleElement.ContentArrayElement(scaling[0]); scaleElement.ContentArrayElement(scaling[1]); scaleElement.ContentArrayElement(scaling[2]); } // If the node has geometry, write out the reference to it. std::map, int>, int>::iterator boneGeometryMapPos = boneGeometryMap.find(std::make_pair(std::make_pair(geometryFileIndex, modelIndex), boneIndex)); if (boneGeometryMapPos != boneGeometryMap.end()) { const std::string& boneGeometryName = boneGeometries[(*boneGeometryMapPos).second].name; XMLWriter::Element instanceGeometryElement(writer, "instance_geometry"); instanceGeometryElement.Attribute("url", std::string("#") + boneGeometryName); BindBoneMaterials(writer, context, materialData, skeletonData, boneIndex, materialMaterialMap, materials, source); } SHelperData dummyHelperData; WriteExtraData(writer, dummyHelperData, skeletonData.GetBoneProperties(boneIndex)); int childIndexCount = skeletonData.GetChildCount(boneIndex); float progressRangeSlice = 1.0f / (childIndexCount > 0 ? float(childIndexCount) : 1.0f); for (int childIndexIndex = 0; childIndexIndex < childIndexCount; ++childIndexIndex) { int childIndex = skeletonData.GetChildIndex(boneIndex, childIndexIndex); WriteSkeletonRecurse(writer, context, modelName, skeletonData, childIndex, bones[childIndex].name, bones, boneGeometryMap, boneGeometries, geometryFileIndex, modelIndex, materialData, materialMaterialMap, materials, source, ProgressRange(progressRange, progressRangeSlice)); } } void WritePhysSkeletonRecurse(XMLWriter& writer, const std::string& modelName, const SkeletonData& skeletonData, int boneIndex, const std::vector& bones, ProgressRange& progressRange, const Matrix34& physFrameTM, const Matrix34& parentTM) { Matrix34 currentPhysFrameTM = physFrameTM; // Output a node for the parent frame. bool shouldWriteParentFrame = skeletonData.HasParentFrame(boneIndex); XMLWriter::Element parentFrameElement(writer, "node", shouldWriteParentFrame); if (shouldWriteParentFrame) { parentFrameElement.Attribute("id", bones[boneIndex].parentFrameName); // The ID must be unique. parentFrameElement.Attribute("name", bones[boneIndex].parentFrameName); // The name must not include model name as prefix, so it can match the skeleton. // Write translation element. float translation[3]; skeletonData.GetParentFrameTranslation(boneIndex, translation); { XMLWriter::Element translateElement(writer, "translate"); translateElement.Attribute("sid", "translation"); translateElement.ContentArrayElement(translation[0]); translateElement.ContentArrayElement(translation[1]); translateElement.ContentArrayElement(translation[2]); } // Write rotation elements. float rotation[3]; skeletonData.GetParentFrameRotation(boneIndex, rotation); for (int axisIndex = 0; axisIndex < 3; ++axisIndex) { XMLWriter::Element rotateElement(writer, "rotate"); char sidBuffer[1024]; sprintf(sidBuffer, "rotation_%c", 'z' - axisIndex); rotateElement.Attribute("sid", sidBuffer); rotateElement.ContentArrayElement(axisIndex == 2 ? 1.0f : 0.0f); rotateElement.ContentArrayElement(axisIndex == 1 ? 1.0f : 0.0f); rotateElement.ContentArrayElement(axisIndex == 0 ? 1.0f : 0.0f); rotateElement.ContentArrayElement(rotation[2 - axisIndex] * 180.0f / 3.14159f); } // Write scale elements float scaling[3]; skeletonData.GetParentFrameScale(boneIndex, scaling); { XMLWriter::Element scaleElement(writer, "scale"); scaleElement.Attribute("sid", "scale"); scaleElement.ContentArrayElement(scaling[0]); scaleElement.ContentArrayElement(scaling[1]); scaleElement.ContentArrayElement(scaling[2]); } Matrix34 tm(IDENTITY); Matrix34 translationTM(IDENTITY); translationTM.SetTranslation(Vec3(translation[0], translation[1], translation[2])); Matrix34 rotationTM = Matrix33::CreateRotationXYZ(Ang3(rotation[0], rotation[1], rotation[2])); Matrix34 scaleTM = Matrix33::CreateScale(Vec3(scaling[0], scaling[1], scaling[2])); Matrix34 transform = translationTM * (rotationTM * scaleTM); currentPhysFrameTM = transform * currentPhysFrameTM; } Matrix34 worldTM; { float translation[3]; skeletonData.GetTranslation(translation, boneIndex); float rotation[3]; skeletonData.GetRotation(rotation, boneIndex); float scaling[3]; skeletonData.GetScale(scaling, boneIndex); Matrix34 tm(IDENTITY); Matrix34 translationTM(IDENTITY); translationTM.SetTranslation(Vec3(translation[0], translation[1], translation[2])); Matrix34 rotationTM = Matrix33::CreateRotationXYZ(Ang3(rotation[0], rotation[1], rotation[2])); Matrix34 scaleTM = Matrix33::CreateScale(Vec3(scaling[0], scaling[1], scaling[2])); worldTM = translationTM * (rotationTM * scaleTM); } Matrix34 transform = parentTM.GetInverted() * worldTM; XMLWriter::Element nodeElement(writer, "node", skeletonData.GetPhysicalized(boneIndex)); if (skeletonData.GetPhysicalized(boneIndex)) { Matrix34 physTM = currentPhysFrameTM.GetInverted() * worldTM; Vec3 translation, scaling; CryQuat orientation; DecomposeTransform(translation, orientation, scaling, physTM); Ang3 rotation = Ang3::GetAnglesXYZ(orientation); nodeElement.Attribute("id", bones[boneIndex].physName); // The ID must be unique. nodeElement.Attribute("name", bones[boneIndex].physName); // The name must not include model name as prefix, so it can match the skeleton. // Write translation element. { XMLWriter::Element translateElement(writer, "translate"); translateElement.Attribute("sid", "translation"); translateElement.ContentArrayElement(translation[0]); translateElement.ContentArrayElement(translation[1]); translateElement.ContentArrayElement(translation[2]); } // Write rotation elements. for (int axisIndex = 0; axisIndex < 3; ++axisIndex) { XMLWriter::Element rotateElement(writer, "rotate"); char sidBuffer[1024]; sprintf(sidBuffer, "rotation_%c", 'z' - axisIndex); rotateElement.Attribute("sid", sidBuffer); rotateElement.ContentArrayElement(axisIndex == 2 ? 1.0f : 0.0f); rotateElement.ContentArrayElement(axisIndex == 1 ? 1.0f : 0.0f); rotateElement.ContentArrayElement(axisIndex == 0 ? 1.0f : 0.0f); rotateElement.ContentArrayElement(rotation[2 - axisIndex] * 180.0f / 3.14159f); } // Write scale elements { XMLWriter::Element scaleElement(writer, "scale"); scaleElement.Attribute("sid", "scale"); scaleElement.ContentArrayElement(scaling[0]); scaleElement.ContentArrayElement(scaling[1]); scaleElement.ContentArrayElement(scaling[2]); } currentPhysFrameTM = worldTM; } SHelperData dummyHelperData; WriteExtraData(writer, dummyHelperData, skeletonData.GetBoneGeomProperties(boneIndex)); int childIndexCount = skeletonData.GetChildCount(boneIndex); float progressRangeSlice = 1.0f / (childIndexCount > 0 ? float(childIndexCount) : 1.0f); for (int childIndexIndex = 0; childIndexIndex < childIndexCount; ++childIndexIndex) { int childIndex = skeletonData.GetChildIndex(boneIndex, childIndexIndex); WritePhysSkeletonRecurse(writer, modelName, skeletonData, childIndex, bones, ProgressRange(progressRange, progressRangeSlice), currentPhysFrameTM, worldTM); } } void WriteGeometryData(XMLWriter& writer, const std::string& id, const std::string& name, GeometryData& geometryData, MaterialData& materialData, std::map& materialMaterialMap, std::vector& materials) { XMLWriter::Element geometryElement(writer, "geometry"); geometryElement.Attribute("id", id); if (!name.empty()) { geometryElement.Attribute("name", name); } XMLWriter::Element meshElement(writer, "mesh"); // Write out the positions. std::string posSourceName = id + "-pos"; { XMLWriter::Element sourceElement(writer, "source"); sourceElement.Attribute("id", posSourceName); std::string arrayName = posSourceName + "-array"; { XMLWriter::Element arrayElement(writer, "float_array"); arrayElement.Attribute("id", arrayName); arrayElement.Attribute("count", int(geometryData.positions.size()) * 3); for (int positionIndex = 0, positionCount = int(geometryData.positions.size()); positionIndex < positionCount; ++positionIndex) { arrayElement.ContentArrayElement(geometryData.positions[positionIndex].x); arrayElement.ContentArrayElement(geometryData.positions[positionIndex].y); arrayElement.ContentArrayElement(geometryData.positions[positionIndex].z); } } XMLWriter::Element techniqueCommonElement(writer, "technique_common"); XMLWriter::Element accessorElement(writer, "accessor"); accessorElement.Attribute("source", std::string("#") + arrayName); accessorElement.Attribute("count", int(geometryData.positions.size())); accessorElement.Attribute("stride", 3); { XMLWriter::Element paramElement(writer, "param"); paramElement.Attribute("name", "X"); paramElement.Attribute("type", "float"); } { XMLWriter::Element paramElement(writer, "param"); paramElement.Attribute("name", "Y"); paramElement.Attribute("type", "float"); } { XMLWriter::Element paramElement(writer, "param"); paramElement.Attribute("name", "Z"); paramElement.Attribute("type", "float"); } } // Write out the normals. std::string normalSourceName = id + "-normal"; { XMLWriter::Element sourceElement(writer, "source"); sourceElement.Attribute("id", normalSourceName); std::string arrayName = normalSourceName + "-array"; { XMLWriter::Element arrayElement(writer, "float_array"); arrayElement.Attribute("id", arrayName); arrayElement.Attribute("count", int(geometryData.normals.size()) * 3); for (int normalIndex = 0, normalCount = int(geometryData.normals.size()); normalIndex < normalCount; ++normalIndex) { arrayElement.ContentArrayElement(geometryData.normals[normalIndex].x); arrayElement.ContentArrayElement(geometryData.normals[normalIndex].y); arrayElement.ContentArrayElement(geometryData.normals[normalIndex].z); } } XMLWriter::Element techniqueCommonElement(writer, "technique_common"); XMLWriter::Element accessorElement(writer, "accessor"); accessorElement.Attribute("source", std::string("#") + arrayName); accessorElement.Attribute("count", int(geometryData.normals.size())); accessorElement.Attribute("stride", 3); { XMLWriter::Element paramElement(writer, "param"); paramElement.Attribute("name", "X"); paramElement.Attribute("type", "float"); } { XMLWriter::Element paramElement(writer, "param"); paramElement.Attribute("name", "Y"); paramElement.Attribute("type", "float"); } { XMLWriter::Element paramElement(writer, "param"); paramElement.Attribute("name", "Z"); paramElement.Attribute("type", "float"); } } // Write out the texture coordinates. std::string textureCoordinateSourceName = id + "-uvs"; if (!geometryData.textureCoordinates.empty()) { XMLWriter::Element sourceElement(writer, "source"); sourceElement.Attribute("id", textureCoordinateSourceName); std::string arrayName = textureCoordinateSourceName + "-array"; { XMLWriter::Element arrayElement(writer, "float_array"); arrayElement.Attribute("id", arrayName); arrayElement.Attribute("count", int(geometryData.textureCoordinates.size()) * 2); for (int textureCoordinateIndex = 0, textureCoordinateCount = int(geometryData.textureCoordinates.size()); textureCoordinateIndex < textureCoordinateCount; ++textureCoordinateIndex) { arrayElement.ContentArrayElement(geometryData.textureCoordinates[textureCoordinateIndex].u); arrayElement.ContentArrayElement(geometryData.textureCoordinates[textureCoordinateIndex].v); } } XMLWriter::Element techniqueCommonElement(writer, "technique_common"); XMLWriter::Element accessorElement(writer, "accessor"); accessorElement.Attribute("source", std::string("#") + arrayName); accessorElement.Attribute("count", int(geometryData.textureCoordinates.size())); accessorElement.Attribute("stride", 2); { XMLWriter::Element paramElement(writer, "param"); paramElement.Attribute("name", "S"); paramElement.Attribute("type", "float"); } { XMLWriter::Element paramElement(writer, "param"); paramElement.Attribute("name", "T"); paramElement.Attribute("type", "float"); } } // Write out the vertex colors. std::string vertexColorSourceName = id + "-vcol"; if (!geometryData.vertexColors.empty()) { XMLWriter::Element sourceElement(writer, "source"); sourceElement.Attribute("id", vertexColorSourceName); std::string arrayName = vertexColorSourceName + "-array"; { XMLWriter::Element arrayElement(writer, "float_array"); arrayElement.Attribute("id", arrayName); arrayElement.Attribute("count", int(geometryData.vertexColors.size()) * 4); for (int vertexColorIndex = 0, vertexColorCount = int(geometryData.vertexColors.size()); vertexColorIndex < vertexColorCount; ++vertexColorIndex) { arrayElement.ContentArrayElement(geometryData.vertexColors[vertexColorIndex].r); arrayElement.ContentArrayElement(geometryData.vertexColors[vertexColorIndex].g); arrayElement.ContentArrayElement(geometryData.vertexColors[vertexColorIndex].b); arrayElement.ContentArrayElement(geometryData.vertexColors[vertexColorIndex].a); } } XMLWriter::Element techniqueCommonElement(writer, "technique_common"); XMLWriter::Element accessorElement(writer, "accessor"); accessorElement.Attribute("source", std::string("#") + arrayName); accessorElement.Attribute("count", int(geometryData.vertexColors.size())); accessorElement.Attribute("stride", 4); { XMLWriter::Element paramElement(writer, "param"); paramElement.Attribute("name", "R"); paramElement.Attribute("type", "float"); } { XMLWriter::Element paramElement(writer, "param"); paramElement.Attribute("name", "G"); paramElement.Attribute("type", "float"); } { XMLWriter::Element paramElement(writer, "param"); paramElement.Attribute("name", "B"); paramElement.Attribute("type", "float"); } { XMLWriter::Element paramElement(writer, "param"); paramElement.Attribute("name", "A"); paramElement.Attribute("type", "float"); } } // Write out the vertex elements. std::string vertexName = id + "-vtx"; { XMLWriter::Element vertexElement(writer, "vertices"); vertexElement.Attribute("id", vertexName); XMLWriter::Element inputElement(writer, "input"); inputElement.Attribute("semantic", "POSITION"); inputElement.Attribute("source", std::string("#") + posSourceName); } // Sort the triangles by material. std::vector > polygonsByMaterial(materialData.GetMaterialCount() + 1); for (int polygonIndex = 0, polygonCount = int(geometryData.polygons.size()); polygonIndex < polygonCount; ++polygonIndex) { polygonsByMaterial[geometryData.polygons[polygonIndex].mtlID + 1].push_back(geometryData.polygons[polygonIndex]); } // Write out the triangles. for (int materialIndex = -1, materialCount = materialData.GetMaterialCount(); materialIndex < materialCount; ++materialIndex) { if (!polygonsByMaterial[materialIndex + 1].empty()) { std::map::iterator materialMapPos = materialMaterialMap.find(materialIndex); int materialEntryIndex = (materialMapPos != materialMaterialMap.end() ? (*materialMapPos).second : -1); std::vector& polygons = polygonsByMaterial[materialIndex + 1]; XMLWriter::Element trianglesElement(writer, "triangles"); trianglesElement.Attribute("count", int(polygons.size())); if (materialEntryIndex >= 0) { trianglesElement.Attribute("material", materials[materialEntryIndex].name); } int offset = 0; bool hasPositions = false; if (!geometryData.positions.empty()) { XMLWriter::Element inputElement(writer, "input"); inputElement.Attribute("semantic", "VERTEX"); inputElement.Attribute("source", std::string("#") + vertexName); inputElement.Attribute("offset", offset++); hasPositions = true; } bool hasNormals = false; if (!geometryData.normals.empty()) { XMLWriter::Element inputElement(writer, "input"); inputElement.Attribute("semantic", "NORMAL"); inputElement.Attribute("source", std::string("#") + normalSourceName); inputElement.Attribute("offset", offset++); hasNormals = true; } bool hasUVs = false; if (!geometryData.textureCoordinates.empty()) { XMLWriter::Element inputElement(writer, "input"); inputElement.Attribute("semantic", "TEXCOORD"); inputElement.Attribute("source", std::string("#") + textureCoordinateSourceName); inputElement.Attribute("offset", offset++); hasUVs = true; } bool hasColors = false; if (!geometryData.vertexColors.empty()) { XMLWriter::Element inputElement(writer, "input"); inputElement.Attribute("semantic", "COLOR"); inputElement.Attribute("source", std::string("#") + vertexColorSourceName); inputElement.Attribute("offset", offset++); hasColors = true; } XMLWriter::Element pElement(writer, "p"); for (int polygonIndex = 0, polygonCount = int(polygons.size()); polygonIndex < polygonCount; ++polygonIndex) { for (int vertexIndex = 0; vertexIndex < 3; ++vertexIndex) { if (hasPositions && polygons[polygonIndex].v[vertexIndex].positionIndex >= 0) { pElement.ContentArrayElement(polygons[polygonIndex].v[vertexIndex].positionIndex); } if (hasNormals && polygons[polygonIndex].v[vertexIndex].normalIndex >= 0) { pElement.ContentArrayElement(polygons[polygonIndex].v[vertexIndex].normalIndex); } if (hasUVs && polygons[polygonIndex].v[vertexIndex].textureCoordinateIndex >= 0) { pElement.ContentArrayElement(polygons[polygonIndex].v[vertexIndex].textureCoordinateIndex); } if (hasColors && polygons[polygonIndex].v[vertexIndex].vertexColorIndex >= 0) { pElement.ContentArrayElement(polygons[polygonIndex].v[vertexIndex].vertexColorIndex); } } } } } } bool WriteGeometries(IExportContext* context, XMLWriter& writer, std::vector& geometries, GeometryFileData& geometryFileData, const std::vector& modelData, MorphDataMap& morphData, MaterialData& materialData, std::vector& materials, std::map& materialMaterialMap, SkeletonDataMap& skeletonData, std::vector& boneGeometries, std::map, int>, int>& boneGeometryMap, std::map, int>, int>& morphGeometryMap, std::vector& morphGeometries, IExportSource* source, ProgressRange& progressRange) { XMLWriter::Element libraryGeometriesElement(writer, "library_geometries"); // Loop through all the geometries. for (int geometryIndex = 0, geometryCount = int(geometries.size()); geometryIndex < geometryCount; ++geometryIndex) { GeometryEntry& geometryEntry = geometries[geometryIndex]; // Read in the geometry data. GeometryData geometryData; bool ok = source->ReadGeometry(context, &geometryData, &modelData[geometryEntry.geometryFileIndex], &materialData, geometryEntry.modelIndex); if (!ok) { return false; } WriteGeometryData(writer, geometryEntry.name, "", geometryData, materialData, materialMaterialMap, materials); } // Loop through all the bone geometries. for (int boneGeometryIndex = 0, boneGeometryCount = int(boneGeometries.size()); boneGeometryIndex < boneGeometryCount; ++boneGeometryIndex) { BoneGeometryEntry& boneGeometryEntry = boneGeometries[boneGeometryIndex]; GeometryData geometryData; SkeletonDataMap::iterator skeletonDataPos = skeletonData.find(std::make_pair(boneGeometryEntry.geometryFileIndex, boneGeometryEntry.modelIndex)); if (skeletonDataPos != skeletonData.end()) { source->ReadBoneGeometry(context, &geometryData, &(*skeletonDataPos).second, boneGeometryEntry.boneIndex, &materialData); } WriteGeometryData(writer, boneGeometryEntry.name, "", geometryData, materialData, materialMaterialMap, materials); } // Loop through all the morph geometries. for (int morphGeometryIndex = 0, morphGeometryCount = int(morphGeometries.size()); morphGeometryIndex < morphGeometryCount; ++morphGeometryIndex) { MorphGeometryEntry& morphGeometryEntry = morphGeometries[morphGeometryIndex]; GeometryData geometryData; MorphDataMap::iterator morphDataPos = morphData.find(std::make_pair(morphGeometryEntry.geometryFileIndex, morphGeometryEntry.modelIndex)); if (morphDataPos != morphData.end()) { source->ReadMorphGeometry(context, &geometryData, &modelData[morphGeometryEntry.geometryFileIndex], morphGeometryEntry.modelIndex, &(*morphDataPos).second, morphGeometryEntry.morphIndex, &materialData); } WriteGeometryData(writer, morphGeometryEntry.name, morphGeometryEntry.morphName, geometryData, materialData, materialMaterialMap, materials); } return true; } void WriteExportNodeProperties(const IExportSource& source, XMLWriter& writer, const char* geomFilename, const IGeometryFileData::SProperties& properties) { XMLWriter::Element elem(writer, "extra"); { XMLWriter::Element elem(writer, "technique"); elem.Attribute("profile", "CryEngine"); { std::string const filetypeStr = ExportFileTypeHelpers::CryFileTypeToString(properties.filetypeInt); std::string props = std::string("fileType=") + filetypeStr; if (properties.bDoNotMerge) { props += std::string("\r\n") + "DoNotMerge"; } if (properties.bUseCustomNormals) { props += std::string("\r\n") + "UseCustomNormals"; } if (properties.filetypeInt == CRY_FILE_TYPE_SKIN && properties.b8WeightsPerVertex) { props += std::string("\r\n") + "EightWeightsPerVertex"; } if (properties.bUseF32VertexFormat) { props += std::string("\r\n") + "UseF32VertexFormat"; } props += std::string("\r\n") + std::string("CustomExportPath=") + properties.customExportPath; XMLWriter::Element elem(writer, "properties"); elem.Content(props); } } // Write special properties for importing to the XSI. { XMLWriter::Element elem(writer, "technique"); elem.Attribute("profile", "XSI"); { XMLWriter::Element elem(writer, "XSI_CustomPSet"); elem.Attribute("name", "ExportProperties"); { XMLWriter::Element elem(writer, "propagation"); elem.Content("NODE"); } { XMLWriter::Element elem(writer, "type"); elem.Content("CryExportNodeProperties"); } { XMLWriter::Element elem(writer, "XSI_Parameter"); elem.Attribute("id", "Filetype"); elem.Attribute("type", "Integer"); elem.Attribute("value", properties.filetypeInt); } { XMLWriter::Element elem(writer, "XSI_Parameter"); elem.Attribute("id", "Filename"); elem.Attribute("type", "Text"); elem.Attribute("value", geomFilename); } { XMLWriter::Element elem(writer, "XSI_Parameter"); elem.Attribute("id", "Exportable"); elem.Attribute("type", "Boolean"); elem.Attribute("value", "1"); } { XMLWriter::Element elem(writer, "XSI_Parameter"); elem.Attribute("id", "MergeObjects"); elem.Attribute("type", "Boolean"); elem.Attribute("value", (!properties.bDoNotMerge)); } } } } void WriteHierarchyRecurse( XMLWriter& writer, IExportContext* context, int geometryFileIndex, MaterialData& materialData, const std::map& materialMaterialMap, const std::vector& materials, const ModelData& modelData, int const modelIndex, std::map, int>& modelGeometryMap, std::vector& geometries, std::map, int>& modelControllerMap, std::vector& controllers, std::map, int>& modelMorphControllerMap, std::vector& morphControllers, IExportSource* source, ProgressRange& progressRange) { XMLWriter::Element nodeElement(writer, "node"); nodeElement.Attribute("id", modelData.GetModelName(modelIndex)); // The ID must be unique. { float translation[3]; float rotation[3]; float scaling[3]; modelData.GetTranslationRotationScale(modelIndex, translation, rotation, scaling); // Write translation element. { XMLWriter::Element translateElement(writer, "translate"); translateElement.Attribute("sid", "translation"); translateElement.ContentArrayElement(translation[0]); translateElement.ContentArrayElement(translation[1]); translateElement.ContentArrayElement(translation[2]); } // Write rotation elements. for (int axisIndex = 0; axisIndex < 3; ++axisIndex) { XMLWriter::Element rotateElement(writer, "rotate"); char sidBuffer[1024]; sprintf(sidBuffer, "rotation_%c", 'z' - axisIndex); rotateElement.Attribute("sid", sidBuffer); rotateElement.ContentArrayElement(axisIndex == 2 ? 1.0f : 0.0f); rotateElement.ContentArrayElement(axisIndex == 1 ? 1.0f : 0.0f); rotateElement.ContentArrayElement(axisIndex == 0 ? 1.0f : 0.0f); rotateElement.ContentArrayElement(rotation[2 - axisIndex] * 180.0f / 3.14159f); } // Write scale elements { XMLWriter::Element scaleElement(writer, "scale"); scaleElement.Attribute("sid", "scale"); scaleElement.ContentArrayElement(scaling[0]); scaleElement.ContentArrayElement(scaling[1]); scaleElement.ContentArrayElement(scaling[2]); } } // If the node has a controller, write out the reference to it. std::map, int>::iterator modelControllerMapPos = modelControllerMap.find(std::make_pair(geometryFileIndex, modelIndex)); std::map, int>::iterator modelMorphControllerMapPos = modelMorphControllerMap.find(std::make_pair(geometryFileIndex, modelIndex)); if (modelControllerMapPos != modelControllerMap.end()) { const std::string& controllerName = controllers[(*modelControllerMapPos).second].name; XMLWriter::Element instanceControllerElement(writer, "instance_controller"); instanceControllerElement.Attribute("url", "#" + controllerName); BindMaterials(writer, context, materialData, modelData, modelIndex, materialMaterialMap, materials, source); } else if (modelMorphControllerMapPos != modelMorphControllerMap.end()) { const std::string& controllerName = morphControllers[(*modelMorphControllerMapPos).second].name; XMLWriter::Element instanceControllerElement(writer, "instance_controller"); instanceControllerElement.Attribute("url", "#" + controllerName); BindMaterials(writer, context, materialData, modelData, modelIndex, materialMaterialMap, materials, source); } else { // If the node has geometry, write out the reference to it. std::map, int>::iterator modelGeometryMapPos = modelGeometryMap.find(std::make_pair(geometryFileIndex, modelIndex)); if (modelGeometryMapPos != modelGeometryMap.end()) { const std::string& geometryName = geometries[(*modelGeometryMapPos).second].name; XMLWriter::Element instanceGeometryElement(writer, "instance_geometry"); instanceGeometryElement.Attribute("url", std::string("#") + geometryName); BindMaterials(writer, context, materialData, modelData, modelIndex, materialMaterialMap, materials, source); } } // Recurse to the child nodes. int childIndexCount = modelData.GetChildCount(modelIndex); float progressRangeSlice = 1.0f / (childIndexCount > 0 ? float(childIndexCount) : 1.0f); for (int childIndexIndex = 0; childIndexIndex < childIndexCount; ++childIndexIndex) { int childIndex = modelData.GetChildIndex(modelIndex, childIndexIndex); WriteHierarchyRecurse(writer, context, geometryFileIndex, materialData, materialMaterialMap, materials, modelData, childIndex, modelGeometryMap, geometries, modelControllerMap, controllers, modelMorphControllerMap, morphControllers, source, ProgressRange(progressRange, progressRangeSlice)); } // Write properties, HelperData WriteExtraData(writer, modelData.GetHelperData(modelIndex), modelData.GetProperties(modelIndex)); } void WriteHierarchy( XMLWriter& writer, IExportContext* context, const GeometryFileData& geometryFileData, MaterialData& materialData, const std::map& materialMaterialMap, const std::vector& materials, const std::vector& modelData, SkeletonDataMap& skeletonData, std::map, int>& modelGeometryMap, std::vector& geometries, std::map, int>& modelControllerMap, std::vector& controllers, BoneDataMap& boneDataMap, std::map, int>, int>& boneGeometryMap, std::vector& boneGeometries, std::map, int>& modelMorphControllerMap, std::vector& morphControllers, IExportSource* source, ProgressRange& progressRange) { XMLWriter::Element libraryVisualScenesElement(writer, "library_visual_scenes"); XMLWriter::Element visualSceneElement(writer, "visual_scene"); visualSceneElement.Attribute("id", "visual_scene_0"); visualSceneElement.Attribute("name", "untitled"); int geometryFileCount = geometryFileData.GetGeometryFileCount(); float geometryFileRangeSlice = 1.0f / (geometryFileCount > 0 ? float(geometryFileCount) : 1.0f); for (int geometryFileIndex = 0; geometryFileIndex < geometryFileCount; ++geometryFileIndex) { ProgressRange geometryFileRange(progressRange, geometryFileRangeSlice); // Make sure to write out a LumberyardExportNode - this is expected by the RC. std::string nodeName; nodeName = geometryFileData.GetGeometryFileName(geometryFileIndex); XMLWriter::Element nodeElement(writer, "node"); nodeElement.Attribute("id", nodeName); nodeElement.Attribute(g_LumberyardExportNodeTag, true); // Write translation element. { XMLWriter::Element translateElement(writer, "translate"); translateElement.Attribute("sid", "translation"); translateElement.Content("0 0 0"); } // Write rotation elements. { XMLWriter::Element rotateElement(writer, "rotate"); rotateElement.Attribute("sid", "rotation_z"); rotateElement.Content("0 0 1 0"); } { XMLWriter::Element rotateElement(writer, "rotate"); rotateElement.Attribute("sid", "rotation_y"); rotateElement.Content("0 1 0 0"); } { XMLWriter::Element rotateElement(writer, "rotate"); rotateElement.Attribute("sid", "rotation_x"); rotateElement.Content("1 0 0 0"); } // Write scale elements { XMLWriter::Element scaleElement(writer, "scale"); scaleElement.Attribute("sid", "scale"); scaleElement.Content("1 1 1"); } { ProgressRange modelProgressRange(geometryFileRange, 0.5f); int rootIndexCount = modelData[geometryFileIndex].GetRootCount(); float progressRangeSlice = 1.0f / (rootIndexCount > 0 ? float(rootIndexCount) : 1.0f); for (int rootIndexIndex = 0; rootIndexIndex < rootIndexCount; ++rootIndexIndex) { int rootModelIndex = modelData[geometryFileIndex].GetRootIndex(rootIndexIndex); WriteHierarchyRecurse(writer, context, geometryFileIndex, materialData, materialMaterialMap, materials, modelData[geometryFileIndex], rootModelIndex, modelGeometryMap, geometries, modelControllerMap, controllers, modelMorphControllerMap, morphControllers, source, ProgressRange(modelProgressRange, progressRangeSlice)); } } { ProgressRange skeletonProgressRange(geometryFileRange, 0.5f); //write the skeleton for the first model in the geometry file only //for (int modelIndex = 0, modelCount = modelData[geometryFileIndex].GetModelCount(); modelIndex < modelCount; ++modelIndex) if (modelData[geometryFileIndex].GetModelCount() > 0) { int modelIndex = 0; int modelCount = 1; SkeletonDataMap::iterator skeletonDataPos = skeletonData.find(std::make_pair(geometryFileIndex, modelIndex)); if (skeletonDataPos != skeletonData.end()) { SkeletonData& skeletonDataInstance = (*skeletonDataPos).second; const std::vector& bones = (*boneDataMap.find(std::make_pair(geometryFileIndex, modelIndex))).second; int rootIndexCount = skeletonDataInstance.GetRootCount(); float progressRangeSlice = 1.0f / ((rootIndexCount > 0 ? float(rootIndexCount) : 1.0f) * modelCount); for (int rootIndexIndex = 0; rootIndexIndex < rootIndexCount; ++rootIndexIndex) { int rootBoneIndex = skeletonDataInstance.GetRootIndex(rootIndexIndex); WriteSkeletonRecurse(writer, context, geometryFileData.GetGeometryFileName(geometryFileIndex), skeletonDataInstance, rootBoneIndex, bones[rootBoneIndex].name, bones, boneGeometryMap, boneGeometries, geometryFileIndex, modelIndex, materialData, materialMaterialMap, materials, source, ProgressRange(skeletonProgressRange, progressRangeSlice * 0.5f)); WritePhysSkeletonRecurse(writer, geometryFileData.GetGeometryFileName(geometryFileIndex), skeletonDataInstance, rootBoneIndex, bones, ProgressRange(skeletonProgressRange, progressRangeSlice * 0.5f), Matrix34(IDENTITY), Matrix34(IDENTITY)); } } } } // Write properties if they exist WriteExportNodeProperties(*source, writer, geometryFileData.GetGeometryFileName(geometryFileIndex), geometryFileData.GetProperties(geometryFileIndex)); } } void WriteMetaData(IExportSource* pExportSource, XMLWriter& writer, ProgressRange& progressRange) { SExportMetaData metaData; pExportSource->GetMetaData(metaData); XMLWriter::Element assetElement(writer, "asset"); { XMLWriter::Element contributorElement(writer, "contributor"); contributorElement.Child("author", metaData.author); contributorElement.Child("authoring_tool", metaData.authoring_tool); contributorElement.Child("source_data", metaData.source_data); } std::time_t time = std::time(0); std::tm dateTime = *std::localtime(&time); char scratchBuffer[1024]; std::strftime(scratchBuffer, sizeof(scratchBuffer) / sizeof(scratchBuffer[0]), "%Y-%m-%dT%H:%M:%SZ", &dateTime); assetElement.Child("created", scratchBuffer); assetElement.Child("modified", scratchBuffer); assetElement.Child("revision", metaData.revision); { XMLWriter::Element unitElement(writer, "unit"); unitElement.Attribute("meter", metaData.fMeterUnit); unitElement.Attribute("name", "meter"); } switch (metaData.up_axis) { case SExportMetaData::X_UP: assetElement.Child("up_axis", "X_UP"); break; case SExportMetaData::Y_UP: assetElement.Child("up_axis", "Y_UP"); break; case SExportMetaData::Z_UP: default: assetElement.Child("up_axis", "Z_UP"); break; } int framesPerSecond = metaData.fFramesPerSecond <= 0.f ? 30 : static_cast(metaData.fFramesPerSecond); sprintf_s(scratchBuffer, sizeof(scratchBuffer) / sizeof(scratchBuffer[0]), "%i", framesPerSecond); XMLWriter::Element frameRateElement(writer, "framerate"); frameRateElement.Attribute("fps", scratchBuffer); } enum AnimationBoneParameter { AnimationBoneParameter_TransX, AnimationBoneParameter_TransY, AnimationBoneParameter_TransZ, AnimationBoneParameter_RotX, AnimationBoneParameter_RotY, AnimationBoneParameter_RotZ, AnimationBoneParameter_SclX, AnimationBoneParameter_SclY, AnimationBoneParameter_SclZ, }; const char* parameterStrings[] = { "posx", "posy", "posz", "rotx", "roty", "rotz", "sclx", "scly", "sclz" }; const char* parameterTargetStrings[] = { "translation.X", "translation.Y", "translation.Z", "rotation_x.ANGLE", "rotation_y.ANGLE", "rotation_z.ANGLE", "scale.X", "scale.Y", "scale.Z" }; struct AnimationBoneParameterEntry { std::string name; int boneIndex; AnimationBoneParameter parameter; }; struct AnimationEntry { std::string name; int geometryFileIndex; int modelIndex; int animationIndex; float start; float stop; std::vector parameters; }; void AddParametersRecursive(AnimationEntry& animation, AnimationData& animationData, const SkeletonData& skeletonData, int boneIndex, ProgressRange& progressRange) { for (AnimationBoneParameter parameter = AnimationBoneParameter_TransX; parameter <= AnimationBoneParameter_SclZ; parameter = AnimationBoneParameter(parameter + 1)) { animation.parameters.push_back(AnimationBoneParameterEntry()); AnimationBoneParameterEntry& parameterEntry = animation.parameters.back(); parameterEntry.boneIndex = boneIndex; parameterEntry.parameter = parameter; parameterEntry.name = animation.name + "-" + skeletonData.GetSafeName(boneIndex) + "_" + parameterStrings[parameter] + "-anim"; // Encode the flags as naming conventions. unsigned modelFlags = animationData.GetModelFlags(boneIndex); if (modelFlags & IAnimationData::ModelFlags_NoExport) { parameterEntry.name += "-NoExport"; } } int childIndexCount = skeletonData.GetChildCount(boneIndex); float progressRangeSlice = 1.0f / (childIndexCount > 0 ? float(childIndexCount) : 1.0f); for (int childIndexIndex = 0; childIndexIndex < childIndexCount; ++childIndexIndex) { AddParametersRecursive(animation, animationData, skeletonData, skeletonData.GetChildIndex(boneIndex, childIndexIndex), ProgressRange(progressRange, progressRangeSlice)); } } void AddParametersForNoSkeleton(AnimationEntry& animation, int modelIndex, const std::string& modelName, AnimationBoneParameter whichParameter) { for (AnimationBoneParameter parameter = whichParameter; parameter < whichParameter + 3; parameter = AnimationBoneParameter(parameter + 1)) { animation.parameters.push_back(AnimationBoneParameterEntry()); AnimationBoneParameterEntry& parameterEntry = animation.parameters.back(); parameterEntry.boneIndex = modelIndex; parameterEntry.parameter = parameter; parameterEntry.name = animation.name + "-" + modelName + "_" + parameterStrings[parameter] + "-anim"; } } void AddAnimationEntry(std::vector& animations, int animationIndex, int geometryFileIndex, int modelIndex, IExportSource* source, GeometryFileData& geometryFileData) { animations.push_back(AnimationEntry()); AnimationEntry& animation = animations.back(); animation.animationIndex = animationIndex; animation.geometryFileIndex = geometryFileIndex; animation.modelIndex = modelIndex; std::string safeAnimationName = source->GetAnimationName(&geometryFileData, geometryFileIndex, animationIndex); std::replace(safeAnimationName.begin(), safeAnimationName.end(), ' ', '_'); animation.name = safeAnimationName + std::string("-") + geometryFileData.GetGeometryFileName(geometryFileIndex); source->GetAnimationTimeSpan(animation.start, animation.stop, animationIndex); } void GenerateAnimationList(IExportContext* context, std::vector& animations, GeometryFileData& geometryFileData, const std::vector& modelData, SkeletonDataMap& skeletonData, IExportSource* source, ProgressRange& progressRange) { int geometryFileCount = geometryFileData.GetGeometryFileCount(); float geometryFileProgressRangeSlice = 1.0f / (geometryFileCount > 0 ? float(geometryFileCount) : 1.0f); for (int geometryFileIndex = 0; geometryFileIndex < geometryFileCount; ++geometryFileIndex) { ProgressRange geometryFileProgressRange(progressRange, geometryFileProgressRangeSlice); int modelCount = modelData[geometryFileIndex].GetModelCount(); float modelProgressRangeSlice = 1.0f / (modelCount > 0 ? float(modelCount) : 1.0f); int animationCount = source->GetAnimationCount(); float animProgressRangeSlice = 1.0f / (animationCount > 0 ? float(animationCount) : 1.0f); for (int animationIndex = 0; animationIndex < animationCount; ++animationIndex) { ProgressRange animationProgressRange(geometryFileProgressRange, animProgressRangeSlice); if (skeletonData.empty()) // Non-skeletal mesh { AddAnimationEntry(animations, animationIndex, geometryFileIndex, -1, source, geometryFileData); } else // Skeletal mesh { AddAnimationEntry(animations, animationIndex, geometryFileIndex, 0, source, geometryFileData); } AnimationEntry& animation = animations.back(); for (int modelIndex = 0; modelIndex < modelCount; ++modelIndex) { ProgressRange modelProgressRange(animationProgressRange, modelProgressRangeSlice); if (skeletonData.empty()) // Non-skeletal mesh { bool hasPos = source->HasValidPosController(&modelData[geometryFileIndex], modelIndex); bool hasRot = source->HasValidRotController(&modelData[geometryFileIndex], modelIndex); bool hasScl = source->HasValidSclController(&modelData[geometryFileIndex], modelIndex); if (hasPos || hasRot || hasScl) { std::string modelName = modelData[geometryFileIndex].GetModelName(modelIndex); std::replace_if(modelName.begin(), modelName.end(), std::isspace, '_'); if (hasPos) { AddParametersForNoSkeleton(animation, modelIndex, modelName, AnimationBoneParameter_TransX); } if (hasRot) { AddParametersForNoSkeleton(animation, modelIndex, modelName, AnimationBoneParameter_RotX); } if (hasScl) { AddParametersForNoSkeleton(animation, modelIndex, modelName, AnimationBoneParameter_SclX); } } } else // Skeletal mesh { ProgressRange animationProgressRange(modelProgressRange, animProgressRangeSlice); // Read the animation flags. SkeletonDataMap::const_iterator skeletonDataPos = skeletonData.find(std::make_pair(geometryFileIndex, modelIndex)); if (skeletonDataPos != skeletonData.end()) { const SkeletonData& skeletonDataInstance = (*skeletonDataPos).second; float FPS = ExportGlobal::g_defaultFrameRate; // This is only used for reading flags, using the default since it will have no impact AnimationData animationData(skeletonDataInstance.GetBoneCount(), FPS, 0); source->ReadAnimationFlags(context, &animationData, &geometryFileData, &modelData[geometryFileIndex], modelIndex, &skeletonDataInstance, animationIndex); int rootIndexCount = skeletonDataInstance.GetRootCount(); float rootProgressRangeSlice = 1.0f / (rootIndexCount > 0 ? float(rootIndexCount) : 1.0f); for (int rootIndexIndex = 0; rootIndexIndex < rootIndexCount; ++rootIndexIndex) { ProgressRange rootProgressRange(animationProgressRange, rootProgressRangeSlice); int rootBoneIndex = skeletonDataInstance.GetRootIndex(rootIndexIndex); // Generate the list of animated parameters for this animation - by generating the names in one place, we can // use them in both passes to refer to each other. AddParametersRecursive(animation, animationData, skeletonDataInstance, rootBoneIndex, rootProgressRange); } } } } } } } void GenerateSkinControllerList(IExportContext* context, std::vector& controllers, std::map, int>& modelControllerMap, SkeletonDataMap& skeletonData, GeometryFileData& geometryFileData, const std::vector& modelData, std::map, int>& modelGeometryMap, std::vector& geometries, ProgressRange& progressRange) { for (int geometryFileIndex = 0, geometryFileCount = geometryFileData.GetGeometryFileCount(); geometryFileIndex < geometryFileCount; ++geometryFileIndex) { for (int modelIndex = 0, modelCount = modelData[geometryFileIndex].GetModelCount(); modelIndex < modelCount; ++modelIndex) { std::map, int>::iterator modelGeometryPos = modelGeometryMap.find(std::make_pair(geometryFileIndex, modelIndex)); SkeletonDataMap::const_iterator skeletonDataPos = skeletonData.find(std::make_pair(geometryFileIndex, modelIndex)); if (skeletonDataPos != skeletonData.end() && modelGeometryPos != modelGeometryMap.end()) { const SkeletonData& skeleton = (*skeletonDataPos).second; int controllerIndex = int(controllers.size()); controllers.resize(controllers.size() + 1); SkinControllerEntry& entry = controllers.back(); char nameBuf[1024]; sprintf(nameBuf, "controller_%d", controllerIndex); entry.name = nameBuf; entry.geometryFileIndex = geometryFileIndex; entry.modelIndex = modelIndex; modelControllerMap.insert(std::make_pair(std::make_pair(geometryFileIndex, modelIndex), controllerIndex)); } } } } void GenerateMorphControllerList(IExportContext* context, std::vector& morphControllers, std::map, int>& modelMorphControllerMap, MorphDataMap& morphData, GeometryFileData& geometryFileData, const std::vector& modelData, std::map, int>& modelGeometryMap, std::vector& geometries, ProgressRange& progressRange) { for (int geometryFileIndex = 0, geometryFileCount = geometryFileData.GetGeometryFileCount(); geometryFileIndex < geometryFileCount; ++geometryFileIndex) { for (int modelIndex = 0, modelCount = modelData[geometryFileIndex].GetModelCount(); modelIndex < modelCount; ++modelIndex) { std::map, int>::iterator modelGeometryPos = modelGeometryMap.find(std::make_pair(geometryFileIndex, modelIndex)); MorphDataMap::const_iterator morphDataPos = morphData.find(std::make_pair(geometryFileIndex, modelIndex)); if (morphDataPos != morphData.end() && modelGeometryPos != modelGeometryMap.end()) { int controllerIndex = int(morphControllers.size()); morphControllers.resize(morphControllers.size() + 1); MorphControllerEntry& entry = morphControllers.back(); char nameBuf[1024]; sprintf(nameBuf, "morphController_%d", controllerIndex); entry.name = nameBuf; entry.geometryFileIndex = geometryFileIndex; entry.modelIndex = modelIndex; modelMorphControllerMap.insert(std::make_pair(std::make_pair(geometryFileIndex, modelIndex), controllerIndex)); } } } } void GenerateEffectsList(IExportContext* context, std::map& materialFXMap, std::vector& effects, MaterialData& materialData) { for (int materialIndex = 0, materialCount = materialData.GetMaterialCount(); materialIndex < materialCount; ++materialIndex) { std::string name; std::string mtlName = materialData.GetName(materialIndex); assert(!mtlName.empty()); name = mtlName; char buffer[100]; const int id = materialData.GetID(materialIndex); assert(id >= 0); sprintf(buffer, "-%d", id + 1); name += buffer; name += "-submat"; name += "-effect"; const int effectIndex = int(effects.size()); effects.push_back(EffectsEntry(name)); materialFXMap.insert(std::make_pair(materialIndex, effectIndex)); } } void GenerateMaterialList(IExportContext* context, std::map& materialMaterialMap, std::map& materialFXMap, std::vector& effects, std::vector& materials, MaterialData& materialData) { for (int materialIndex = 0, materialCount = materialData.GetMaterialCount(); materialIndex < materialCount; ++materialIndex) { // Material needs to be named according to a specific format - this communicates information to // the resource compiler about the settings to be used for the material. // Format is: ____[__...] std::string name; std::string mtlName = materialData.GetName(materialIndex); assert(!mtlName.empty()); std::string mtlProperties = materialData.GetProperties(materialIndex); assert(!mtlProperties.empty()); name = mtlName; char buffer[100]; const int id = materialData.GetID(materialIndex); assert(id >= 0); sprintf(buffer, "__%d", id + 1); name += buffer; name += "__"; name += materialData.GetSubMatName(materialIndex); name += mtlProperties; const int index = int(materials.size()); materials.push_back(MaterialEntry(name)); materialMaterialMap.insert(std::make_pair(materialIndex, index)); } } void GenerateGeometryList(IExportContext* context, std::map, int>& modelGeometryMap, std::vector& geometries, GeometryFileData& geometryFileData, const std::vector& modelData) { for (int geometryFileIndex = 0, geometryFileCount = geometryFileData.GetGeometryFileCount(); geometryFileIndex < geometryFileCount; ++geometryFileIndex) { for (int modelIndex = 0, modelCount = modelData[geometryFileIndex].GetModelCount(); modelIndex < modelCount; ++modelIndex) { if (modelData[geometryFileIndex].HasGeometry(modelIndex)) { std::string geometryName = std::string(geometryFileData.GetGeometryFileName(geometryFileIndex)) + "_" + modelData[geometryFileIndex].GetModelName(modelIndex) + "_geometry"; int geometryIndex = int(geometries.size()); geometries.push_back(GeometryEntry(geometryName, geometryFileIndex, modelIndex)); modelGeometryMap.insert(std::make_pair(std::make_pair(geometryFileIndex, modelIndex), geometryIndex)); } } } } void GenerateBoneGeometryList(IExportContext* context, std::map, int>, int>& boneGeometryMap, std::vector& boneGeometries, GeometryFileData& geometryFileData, const std::vector& modelData, SkeletonDataMap& skeletonData) { for (int geometryFileIndex = 0, geometryFileCount = geometryFileData.GetGeometryFileCount(); geometryFileIndex < geometryFileCount; ++geometryFileIndex) { for (int modelIndex = 0, modelCount = modelData[geometryFileIndex].GetModelCount(); modelIndex < modelCount; ++modelIndex) { SkeletonDataMap::iterator skeletonDataPos = skeletonData.find(std::make_pair(geometryFileIndex, modelIndex)); if (skeletonDataPos != skeletonData.end()) { SkeletonData& modelSkeletonData = (*skeletonDataPos).second; for (int boneIndex = 0, boneCount = modelSkeletonData.GetBoneCount(); boneIndex < boneCount; ++boneIndex) { if (modelSkeletonData.HasGeometry(boneIndex)) { std::string geometryName = std::string(geometryFileData.GetGeometryFileName(geometryFileIndex)) + "_" + modelData[geometryFileIndex].GetModelName(modelIndex) + "_" + modelSkeletonData.GetSafeName(boneIndex) + "_boneGeometry"; int geometryIndex = int(boneGeometries.size()); boneGeometries.push_back(BoneGeometryEntry(geometryName, geometryFileIndex, modelIndex, boneIndex)); boneGeometryMap.insert(std::make_pair(std::make_pair(std::make_pair(geometryFileIndex, modelIndex), boneIndex), geometryIndex)); } } } } } } void GenerateMorphGeometryList(IExportContext* context, std::map, int>, int>& morphGeometryMap, std::vector& morphGeometries, GeometryFileData& geometryFileData, const std::vector& modelData, MorphDataMap& morphData) { for (int geometryFileIndex = 0, geometryFileCount = geometryFileData.GetGeometryFileCount(); geometryFileIndex < geometryFileCount; ++geometryFileIndex) { for (int modelIndex = 0, modelCount = modelData[geometryFileIndex].GetModelCount(); modelIndex < modelCount; ++modelIndex) { MorphDataMap::iterator morphDataPos = morphData.find(std::make_pair(geometryFileIndex, modelIndex)); if (morphDataPos != morphData.end()) { MorphData& modelMorphData = (*morphDataPos).second; for (int morphIndex = 0, morphCount = modelMorphData.GetMorphCount(); morphIndex < morphCount; ++morphIndex) { std::string geometryName = std::string(geometryFileData.GetGeometryFileName(geometryFileIndex)) + "_" + modelData[geometryFileIndex].GetModelName(modelIndex) + "_" + modelMorphData.GetMorphFullName(morphIndex) + "_morphGeometry"; int geometryIndex = int(morphGeometries.size()); morphGeometries.push_back(MorphGeometryEntry(geometryName, modelMorphData.GetMorphName(morphIndex), geometryFileIndex, modelIndex, morphIndex)); morphGeometryMap.insert(std::make_pair(std::make_pair(std::make_pair(geometryFileIndex, modelIndex), morphIndex), geometryIndex)); } } } } } void GenerateIKPropertyList(const SkeletonData& skeletonData, int boneIndex, std::vector >& propertyList) { // Loop through each axis, adding the properties for this axis to the list. for (int axis = 0; axis < 3; ++axis) { // Add the limit properties for this axis. const char* extremeNames[] = {"min", "max"}; for (int extreme = 0; extreme < 2; ++extreme) { std::string key; key += ('x' + axis); key += extremeNames[extreme]; if (skeletonData.HasLimit(boneIndex, ISkeletonData::Axis(axis), ISkeletonData::Limit(extreme))) { float limit = skeletonData.GetLimit(boneIndex, ISkeletonData::Axis(axis), ISkeletonData::Limit(extreme)); char buffer[1024]; sprintf(buffer, "%f", limit * 180.0f / 3.14159f); // Convert to degrees. propertyList.push_back(std::make_pair(key, buffer)); } } // Add the remaining properties. const char* propNames[] = {"damping", "springangle", "springtension"}; typedef bool (SkeletonData::* HasMember)(int boneIndex, ISkeletonData::Axis axis) const; typedef float (SkeletonData::* GetMember)(int boneIndex, ISkeletonData::Axis axis) const; HasMember hasMembers[] = {&SkeletonData::HasAxisDamping, &SkeletonData::HasSpringAngle, &SkeletonData::HasSpringTension}; GetMember getMembers[] = {&SkeletonData::GetAxisDamping, &SkeletonData::GetSpringAngle, &SkeletonData::GetSpringTension}; for (int propIndex = 0; propIndex < 3; ++propIndex) { std::string key; key += ('x' + axis); key += propNames[propIndex]; if ((skeletonData.*(hasMembers[propIndex]))(boneIndex, ISkeletonData::Axis(axis))) { float value = (skeletonData.*(getMembers[propIndex]))(boneIndex, ISkeletonData::Axis(axis)); char buffer[1024]; sprintf(buffer, "%f", value); propertyList.push_back(std::make_pair(key, buffer)); } } } } void GenerateBoneList(IExportContext* context, BoneDataMap& boneDataMap, const SkeletonDataMap& skeletonData, const std::vector& modelData) { for (SkeletonDataMap::const_iterator skeletonDataPos = skeletonData.begin(), skeletonDataEnd = skeletonData.end(); skeletonDataPos != skeletonDataEnd; ++skeletonDataPos) { int geometryFileIndex = (*skeletonDataPos).first.first; int modelIndex = (*skeletonDataPos).first.second; const SkeletonData& skeleton = (*skeletonDataPos).second; BoneDataMap::iterator boneDataPos = boneDataMap.insert(std::make_pair(std::make_pair(geometryFileIndex, modelIndex), std::vector())).first; std::vector& bones = (*boneDataPos).second; bones.resize(skeleton.GetBoneCount()); std::string modelName = modelData[geometryFileIndex].GetModelName(modelIndex); for (int boneIndex = 0, boneCount = skeleton.GetBoneCount(); boneIndex < boneCount; ++boneIndex) { typedef std::string BoneEntry::* BoneNamePtr; BoneNamePtr boneNames[3] = {&BoneEntry::name, &BoneEntry::physName, &BoneEntry::parentFrameName}; const char* suffixes[3] = {"", " Phys", " Phys ParentFrame"}; std::vector > properties[3]; // Add the IK properties to the phys bone. GenerateIKPropertyList(skeleton, boneIndex, properties[1]); for (int nameIndex = 0; nameIndex < 3; ++nameIndex) { std::string unsafeName = skeleton.GetName(boneIndex); unsafeName += suffixes[nameIndex]; bool containsSpaces = (unsafeName.find_first_of(" \t") != std::string::npos); std::string name = unsafeName; if (containsSpaces) { std::string overrideName = unsafeName; std::replace_if(overrideName.begin(), overrideName.end(), std::isspace, '*'); std::string safeName = unsafeName; std::replace_if(safeName.begin(), safeName.end(), std::isspace, '_'); name = safeName + "%" + modelName + "%" + "--PRprops_name=" + overrideName; // Add all the properties. for (size_t propIndex = 0, propCount = properties[nameIndex].size(); propIndex < propCount; ++propIndex) { name += "_" + properties[nameIndex][propIndex].first + "=" + properties[nameIndex][propIndex].second; } name += "__"; } else { name = unsafeName + "%" + modelName + "%"; } (bones[boneIndex].*(boneNames[nameIndex])) = name; } } } } void WriteAnimationList(XMLWriter& writer, std::vector& animations, ProgressRange& progressRange) { // Write out all the animations in the library_animation_clips element. Each animation lists the name and timespan // of the clip, and the controllers for each model parameter. The actual animation data is written out in a separate pass. XMLWriter::Element libraryAnimationClipsElement(writer, "library_animation_clips"); for (int animationEntryIndex = 0, animationEntryCount = int(animations.size()); animationEntryIndex < animationEntryCount; ++animationEntryIndex) { AnimationEntry& entry = animations[animationEntryIndex]; XMLWriter::Element animationClipElement(writer, "animation_clip"); animationClipElement.Attribute("start", entry.start); animationClipElement.Attribute("end", entry.stop); animationClipElement.Attribute("id", entry.name); // For each parameter write out a reference to the controller for that parameter. for (int parameterEntryIndex = 0, parameterEntryCount = int(entry.parameters.size()); parameterEntryIndex < parameterEntryCount; ++parameterEntryIndex) { AnimationBoneParameterEntry& parameter = entry.parameters[parameterEntryIndex]; XMLWriter::Element instanceAnimationElement(writer, "instance_animation"); instanceAnimationElement.Attribute("url", std::string("#") + parameter.name); } } } void WriteAnimationTags( ProgressRange& animationEntryProgressRange, const AnimationEntry& entry, const IAnimationData* animationData, XMLWriter& writer, const std::vector* pBones, const IModelData* pModelData) { // Loop through all the parameters of all the models. ProgressRange writeAnimProgressRange(animationEntryProgressRange, 0.5f); for (int parameterEntryIndex = 0, parameterEntryCount = int(entry.parameters.size()); parameterEntryIndex < parameterEntryCount; ++parameterEntryIndex) { const AnimationBoneParameterEntry& parameter = entry.parameters[parameterEntryIndex]; int frameCount = 0; switch (parameter.parameter) { case AnimationBoneParameter_TransX: case AnimationBoneParameter_TransY: case AnimationBoneParameter_TransZ: frameCount = animationData->GetFrameCountPos(parameter.boneIndex); break; case AnimationBoneParameter_RotX: case AnimationBoneParameter_RotY: case AnimationBoneParameter_RotZ: frameCount = animationData->GetFrameCountRot(parameter.boneIndex); break; case AnimationBoneParameter_SclX: case AnimationBoneParameter_SclY: case AnimationBoneParameter_SclZ: frameCount = animationData->GetFrameCountScl(parameter.boneIndex); break; } XMLWriter::Element animationElement(writer, "animation"); animationElement.Attribute("id", parameter.name); std::string inputID = parameter.name + "-input"; std::string outputID = parameter.name + "-output"; std::string interpID = parameter.name + "-interp"; std::string tcbID = parameter.name + "-tcb"; std::string easeinoutID = parameter.name + "-easeinout"; // Write out the times. { XMLWriter::Element inputElement(writer, "source"); inputElement.Attribute("id", inputID); std::string arrayID = inputID + "-array"; { XMLWriter::Element array(writer, "float_array"); array.Attribute("count", frameCount); array.Attribute("id", arrayID); float floatBuffer[24]; int bufferCount = 0; for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) { switch (parameter.parameter) { case AnimationBoneParameter_TransX: case AnimationBoneParameter_TransY: case AnimationBoneParameter_TransZ: floatBuffer[bufferCount++] = animationData->GetFrameTimePos(parameter.boneIndex, frameIndex); break; case AnimationBoneParameter_RotX: case AnimationBoneParameter_RotY: case AnimationBoneParameter_RotZ: floatBuffer[bufferCount++] = animationData->GetFrameTimeRot(parameter.boneIndex, frameIndex); break; case AnimationBoneParameter_SclX: case AnimationBoneParameter_SclY: case AnimationBoneParameter_SclZ: floatBuffer[bufferCount++] = animationData->GetFrameTimeScl(parameter.boneIndex, frameIndex); break; } if (bufferCount == 24) { array.ContentArrayFloat24(floatBuffer, bufferCount); bufferCount = 0; } } if (bufferCount > 0) { array.ContentArrayFloat24(floatBuffer, bufferCount); bufferCount = 0; } } XMLWriter::Element techniqueCommonElement(writer, "technique_common"); XMLWriter::Element accessorElement(writer, "accessor"); accessorElement.Attribute("source", arrayID); accessorElement.Attribute("count", frameCount); accessorElement.Attribute("stride", 1); XMLWriter::Element paramElement(writer, "param"); paramElement.Attribute("name", "TIME"); paramElement.Attribute("type", "float"); } // Write out the values. { XMLWriter::Element inputElement(writer, "source"); inputElement.Attribute("id", outputID); std::string arrayID = outputID + "-array"; { XMLWriter::Element array(writer, "float_array"); array.Attribute("count", frameCount); array.Attribute("id", arrayID); float floatBuffer[24]; int bufferCount = 0; for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) { const float* translation, * rotation, * scale; switch (parameter.parameter) { case AnimationBoneParameter_TransX: case AnimationBoneParameter_TransY: case AnimationBoneParameter_TransZ: animationData->GetFrameDataPos(parameter.boneIndex, frameIndex, translation); floatBuffer[bufferCount++] = translation[parameter.parameter - AnimationBoneParameter_TransX]; break; case AnimationBoneParameter_RotX: case AnimationBoneParameter_RotY: case AnimationBoneParameter_RotZ: animationData->GetFrameDataRot(parameter.boneIndex, frameIndex, rotation); floatBuffer[bufferCount++] = rotation[parameter.parameter - AnimationBoneParameter_RotX]; break; case AnimationBoneParameter_SclX: case AnimationBoneParameter_SclY: case AnimationBoneParameter_SclZ: animationData->GetFrameDataScl(parameter.boneIndex, frameIndex, scale); floatBuffer[bufferCount++] = scale[parameter.parameter - AnimationBoneParameter_SclX]; break; } if (bufferCount == 24) { array.ContentArrayFloat24(floatBuffer, bufferCount); bufferCount = 0; } } if (bufferCount > 0) { array.ContentArrayFloat24(floatBuffer, bufferCount); bufferCount = 0; } } XMLWriter::Element techniqueCommonElement(writer, "technique_common"); XMLWriter::Element accessorElement(writer, "accessor"); accessorElement.Attribute("source", arrayID); accessorElement.Attribute("count", frameCount); accessorElement.Attribute("stride", 1); XMLWriter::Element paramElement(writer, "param"); paramElement.Attribute("name", "VALUE"); paramElement.Attribute("type", "float"); } // Write out the interpolation method. { XMLWriter::Element inputElement(writer, "source"); inputElement.Attribute("id", interpID); std::string arrayID = interpID + "-array"; { XMLWriter::Element array(writer, "Name_array"); array.Attribute("count", frameCount); array.Attribute("id", arrayID); for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) { array.WriteDirectText(" CONSTANT"); } } XMLWriter::Element techniqueCommonElement(writer, "technique_common"); XMLWriter::Element accessorElement(writer, "accessor"); accessorElement.Attribute("source", arrayID); accessorElement.Attribute("count", frameCount); accessorElement.Attribute("stride", 1); XMLWriter::Element paramElement(writer, "param"); paramElement.Attribute("name", "INTERPOLATION"); paramElement.Attribute("type", "Name"); } if (pModelData) // only for the non-skeletal animation { // Write out the TCB values. { const int stride = 3; XMLWriter::Element inputElement(writer, "source"); inputElement.Attribute("id", tcbID); std::string arrayID = tcbID + "-array"; { XMLWriter::Element array(writer, "float_array"); array.Attribute("count", frameCount * stride); array.Attribute("id", arrayID); for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) { IAnimationData::TCB tcb; switch (parameter.parameter) { case AnimationBoneParameter_TransX: case AnimationBoneParameter_TransY: case AnimationBoneParameter_TransZ: animationData->GetFrameTCBPos(parameter.boneIndex, frameIndex, tcb); break; case AnimationBoneParameter_RotX: case AnimationBoneParameter_RotY: case AnimationBoneParameter_RotZ: animationData->GetFrameTCBRot(parameter.boneIndex, frameIndex, tcb); break; case AnimationBoneParameter_SclX: case AnimationBoneParameter_SclY: case AnimationBoneParameter_SclZ: animationData->GetFrameTCBScl(parameter.boneIndex, frameIndex, tcb); break; } array.ContentArrayElement(tcb.tension); array.ContentArrayElement(tcb.continuity); array.ContentArrayElement(tcb.bias); } } XMLWriter::Element techniqueCommonElement(writer, "technique_common"); XMLWriter::Element accessorElement(writer, "accessor"); accessorElement.Attribute("source", arrayID); accessorElement.Attribute("count", frameCount); accessorElement.Attribute("stride", stride); { XMLWriter::Element paramElement(writer, "param"); paramElement.Attribute("name", "TENSION"); paramElement.Attribute("type", "float"); } { XMLWriter::Element paramElement(writer, "param"); paramElement.Attribute("name", "CONTINUITY"); paramElement.Attribute("type", "float"); } { XMLWriter::Element paramElement(writer, "param"); paramElement.Attribute("name", "BIAS"); paramElement.Attribute("type", "float"); } } // Write out the ease-in/-out values. { const int stride = 2; XMLWriter::Element inputElement(writer, "source"); inputElement.Attribute("id", easeinoutID); std::string arrayID = easeinoutID + "-array"; { XMLWriter::Element array(writer, "float_array"); array.Attribute("count", frameCount * stride); array.Attribute("id", arrayID); for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) { IAnimationData::Ease ease; switch (parameter.parameter) { case AnimationBoneParameter_TransX: case AnimationBoneParameter_TransY: case AnimationBoneParameter_TransZ: animationData->GetFrameEaseInOutPos(parameter.boneIndex, frameIndex, ease); break; case AnimationBoneParameter_RotX: case AnimationBoneParameter_RotY: case AnimationBoneParameter_RotZ: animationData->GetFrameEaseInOutRot(parameter.boneIndex, frameIndex, ease); break; case AnimationBoneParameter_SclX: case AnimationBoneParameter_SclY: case AnimationBoneParameter_SclZ: animationData->GetFrameEaseInOutScl(parameter.boneIndex, frameIndex, ease); break; } array.ContentArrayElement(ease.in); array.ContentArrayElement(ease.out); } } XMLWriter::Element techniqueCommonElement(writer, "technique_common"); XMLWriter::Element accessorElement(writer, "accessor"); accessorElement.Attribute("source", arrayID); accessorElement.Attribute("count", frameCount); accessorElement.Attribute("stride", stride); { XMLWriter::Element paramElement(writer, "param"); paramElement.Attribute("name", "EASE_IN"); paramElement.Attribute("type", "float"); } { XMLWriter::Element paramElement(writer, "param"); paramElement.Attribute("name", "EASE_OUT"); paramElement.Attribute("type", "float"); } } } // Write out the sampler element. std::string samplerID = parameter.name + "-sampler"; { XMLWriter::Element samplerElement(writer, "sampler"); samplerElement.Attribute("id", samplerID); { XMLWriter::Element inputElement(writer, "input"); inputElement.Attribute("semantic", "INPUT"); inputElement.Attribute("source", std::string("#") + inputID); } { XMLWriter::Element inputElement(writer, "input"); inputElement.Attribute("semantic", "OUTPUT"); inputElement.Attribute("source", std::string("#") + outputID); } { XMLWriter::Element inputElement(writer, "input"); inputElement.Attribute("semantic", "INTERPOLATION"); inputElement.Attribute("source", std::string("#") + interpID); } if (pModelData) // only for the non-skeletal animation { { XMLWriter::Element inputElement(writer, "input"); inputElement.Attribute("semantic", "TCB"); inputElement.Attribute("source", std::string("#") + tcbID); } { XMLWriter::Element inputElement(writer, "input"); inputElement.Attribute("semantic", "EASE_IN_OUT"); inputElement.Attribute("source", std::string("#") + easeinoutID); } } } // Write out the channel element. XMLWriter::Element channelElement(writer, "channel"); channelElement.Attribute("source", std::string("#") + samplerID); std::string targetName; if (pBones) { targetName = (*pBones)[parameter.boneIndex].name; } else { assert(pModelData); targetName = pModelData->GetModelName(parameter.boneIndex); } targetName = targetName + "/" + parameterTargetStrings[parameter.parameter]; channelElement.Attribute("target", targetName); } } void WriteAnimationData( IExportContext* context, XMLWriter& writer, std::vector& animations, GeometryFileData& geometryFileData, const std::vector& modelData, SkeletonDataMap& skeletonData, const BoneDataMap& boneDataMap, IExportSource* source, ProgressRange& progressRange) { XMLWriter::Element libraryAnimationsElement(writer, "library_animations"); int animationEntryCount = int(animations.size()); float animationEntryProgressSlice = (1.0f / (animationEntryCount ? float(animationEntryCount) : 1.0f)); for (int animationEntryIndex = 0; animationEntryIndex < animationEntryCount; ++animationEntryIndex) { float FPS = source->GetDCCFrameRate(); ProgressRange animationEntryProgressRange(progressRange, animationEntryProgressSlice); AnimationEntry& entry = animations[animationEntryIndex]; if (skeletonData.empty()) // Non-skeletal mesh { IAnimationData* animationData = NULL; { ProgressRange readAnimProgressRange(animationEntryProgressRange, 0.5f); animationData = source->ReadAnimation(context, &geometryFileData, &modelData[0], -1, NULL, animationEntryIndex, FPS); } if (animationData) { WriteAnimationTags(animationEntryProgressRange, entry, animationData, writer, NULL, &modelData[0]); delete animationData; } } else // Skeletal mesh { // Read the animation data. SkeletonDataMap::const_iterator skeletonDataPos = skeletonData.find(std::make_pair(entry.geometryFileIndex, entry.modelIndex)); if (skeletonDataPos != skeletonData.end()) { IAnimationData* animationData = NULL; const SkeletonData& skeletonDataInstance = (*skeletonDataPos).second; { ProgressRange readAnimProgressRange(animationEntryProgressRange, 0.5f); animationData = source->ReadAnimation(context, &geometryFileData, &modelData[entry.modelIndex], entry.modelIndex, &skeletonDataInstance, entry.animationIndex, FPS); } // Look up the bone entries for this model. BoneDataMap::const_iterator boneDataPos = boneDataMap.find(std::make_pair(entry.geometryFileIndex, entry.modelIndex)); const std::vector& bones = (*boneDataPos).second; if (animationData) { WriteAnimationTags(animationEntryProgressRange, entry, animationData, writer, &bones, NULL); delete animationData; } } } } } void WriteEffects(XMLWriter& writer, std::vector& effects, ProgressRange& progressRange) { XMLWriter::Element libraryEffectsElement(writer, "library_effects"); for (int effectIndex = 0, effectCount = int(effects.size()); effectIndex < effectCount; ++effectIndex) { XMLWriter::Element effectElement(writer, "effect"); effectElement.Attribute("id", effects[effectIndex].name); // Write out dummy effects values. XMLWriter::Element profileElement(writer, "profile_COMMON"); XMLWriter::Element techniqueElement(writer, "technique"); techniqueElement.Attribute("sid", "default"); XMLWriter::Element phongElement(writer, "phong"); { XMLWriter::Element emissionElement(writer, "emission"); XMLWriter::Element colorElement(writer, "color"); colorElement.Attribute("sid", "emission"); colorElement.ContentArrayElement(0.0f); colorElement.ContentArrayElement(0.0f); colorElement.ContentArrayElement(0.0f); colorElement.ContentArrayElement(1.0f); } { XMLWriter::Element ambientElement(writer, "ambient"); XMLWriter::Element colorElement(writer, "color"); colorElement.Attribute("sid", "ambient"); colorElement.ContentArrayElement(0.0f); colorElement.ContentArrayElement(0.0f); colorElement.ContentArrayElement(0.0f); colorElement.ContentArrayElement(1.0f); } { XMLWriter::Element diffuseElement(writer, "diffuse"); XMLWriter::Element colorElement(writer, "color"); colorElement.Attribute("sid", "diffuse"); colorElement.ContentArrayElement(0.0f); colorElement.ContentArrayElement(0.0f); colorElement.ContentArrayElement(0.0f); colorElement.ContentArrayElement(1.0f); } { XMLWriter::Element specularElement(writer, "specular"); XMLWriter::Element colorElement(writer, "color"); colorElement.Attribute("sid", "specular"); colorElement.ContentArrayElement(0.0f); colorElement.ContentArrayElement(0.0f); colorElement.ContentArrayElement(0.0f); colorElement.ContentArrayElement(1.0f); } { XMLWriter::Element shininessElement(writer, "shininess"); XMLWriter::Element floatElement(writer, "float"); floatElement.Attribute("sid", "shininess"); floatElement.ContentArrayElement(0.0f); } { XMLWriter::Element reflectiveElement(writer, "reflective"); XMLWriter::Element colorElement(writer, "color"); colorElement.Attribute("sid", "reflective"); colorElement.ContentArrayElement(0.0f); colorElement.ContentArrayElement(0.0f); colorElement.ContentArrayElement(0.0f); colorElement.ContentArrayElement(1.0f); } { XMLWriter::Element reflectivityElement(writer, "reflectivity"); XMLWriter::Element floatElement(writer, "float"); floatElement.Attribute("sid", "reflectivity"); floatElement.ContentArrayElement(0.0f); } { XMLWriter::Element transparentElement(writer, "transparent"); transparentElement.Attribute("opaque", "RGB_ZERO"); XMLWriter::Element colorElement(writer, "color"); colorElement.Attribute("sid", "transparent"); colorElement.ContentArrayElement(0.0f); colorElement.ContentArrayElement(0.0f); colorElement.ContentArrayElement(0.0f); colorElement.ContentArrayElement(0.0f); } { XMLWriter::Element transparencyElement(writer, "transparency"); XMLWriter::Element floatElement(writer, "float"); floatElement.Attribute("sid", "transparency"); floatElement.ContentArrayElement(0.0f); } { XMLWriter::Element refractionElement(writer, "index_of_refraction"); XMLWriter::Element floatElement(writer, "float"); floatElement.Attribute("sid", "index_of_refraction"); floatElement.ContentArrayElement(0.0f); } } } void WriteControllers( XMLWriter& writer, IExportContext* context, IExportSource* exportSource, std::vector& skinControllers, std::vector& morphControllers, std::map, int> modelMorphControllerMap, GeometryFileData& geometryFileData, const std::vector& modelData, SkeletonDataMap& skeletonData, MorphDataMap& morphData, std::vector& morphGeometries, std::map, int>, int>& morphGeometryMap, std::vector& geometries, std::map, int>& modelGeometryMap, const BoneDataMap& boneDataMap, ProgressRange& progressRange) { // XMLWriter::Element libraryControllersElement(writer, "library_controllers"); for (int controllerIndex = 0, controllerCount = int(skinControllers.size()); controllerIndex < controllerCount; ++controllerIndex) { SkinControllerEntry& controller = skinControllers[controllerIndex]; SkeletonDataMap::iterator skeletonDataPos = skeletonData.find(std::make_pair(controller.geometryFileIndex, controller.modelIndex)); if (skeletonDataPos == skeletonData.end()) { continue; } SkeletonData skeleton = (*skeletonDataPos).second; // XMLWriter::Element controllerElement(writer, "controller"); controllerElement.Attribute("id", controller.name); // std::string geometryName; bool sourceFound = false; { std::map, int>::const_iterator modelMorphControllerMapPos = modelMorphControllerMap.find(std::make_pair(controller.geometryFileIndex, controller.modelIndex)); int morphControllerIndex = (modelMorphControllerMapPos != modelMorphControllerMap.end() ? (*modelMorphControllerMapPos).second : -1); geometryName = (morphControllerIndex >= 0 ? morphControllers[morphControllerIndex].name : "MISSING MORPH CONTROLLER NAME"); sourceFound = (morphControllerIndex >= 0); } if (!sourceFound) { std::map, int>::const_iterator geometryMapPos = modelGeometryMap.find(std::make_pair(controller.geometryFileIndex, controller.modelIndex)); int geometryIndex = (geometryMapPos != modelGeometryMap.end() ? (*geometryMapPos).second : -1); geometryName = (geometryIndex >= 0 ? geometries[geometryIndex].name : "MISSING GEOMETRY NAME"); sourceFound = (geometryIndex >= 0); } XMLWriter::Element skinElement(writer, "skin"); skinElement.Attribute("source", "#" + geometryName); // // 1.000000 0.000000 0.000000 0.000000 // 0.000000 1.000000 0.000000 0.000000 // 0.000000 0.000000 1.000000 0.000000 // 0.000000 0.000000 0.000000 1.000000 // { XMLWriter::Element bindMatrixElement(writer, "bind_shape_matrix"); bindMatrixElement.ContentArrayElement(1.0f); bindMatrixElement.ContentArrayElement(0.0f); bindMatrixElement.ContentArrayElement(0.0f); bindMatrixElement.ContentArrayElement(0.0f); bindMatrixElement.ContentArrayElement(0.0f); bindMatrixElement.ContentArrayElement(1.0f); bindMatrixElement.ContentArrayElement(0.0f); bindMatrixElement.ContentArrayElement(0.0f); bindMatrixElement.ContentArrayElement(0.0f); bindMatrixElement.ContentArrayElement(0.0f); bindMatrixElement.ContentArrayElement(1.0f); bindMatrixElement.ContentArrayElement(0.0f); bindMatrixElement.ContentArrayElement(0.0f); bindMatrixElement.ContentArrayElement(0.0f); bindMatrixElement.ContentArrayElement(0.0f); bindMatrixElement.ContentArrayElement(1.0f); } // std::string jointsSourceName = controller.name + "_joints"; { XMLWriter::Element jointsSourceElement(writer, "source"); jointsSourceElement.Attribute("id", jointsSourceName); // const std::vector& bones = (*boneDataMap.find(std::make_pair(controller.geometryFileIndex, controller.modelIndex))).second; std::string arrayName = jointsSourceName + "_array"; { XMLWriter::Element idArrayElement(writer, "IDREF_array"); idArrayElement.Attribute("id", arrayName); idArrayElement.Attribute("count", skeleton.GetBoneCount()); for (int boneIndex = 0, boneCount = skeleton.GetBoneCount(); boneIndex < boneCount; ++boneIndex) { idArrayElement.ContentArrayElement(bones[boneIndex].name); } } // { XMLWriter::Element techniqueCommonElement(writer, "technique_common"); // XMLWriter::Element accessorElement(writer, "accessor"); accessorElement.Attribute("count", skeleton.GetBoneCount()); accessorElement.Attribute("stride", 1); accessorElement.Attribute("source", "#" + arrayName); // XMLWriter::Element paramElement(writer, "param"); paramElement.Attribute("type", std::string("IDREF")); // // // } } // std::string matricesSourceName = controller.name + "_matrices"; { XMLWriter::Element sourceElement(writer, "source"); sourceElement.Attribute("id", matricesSourceName); // std::string arrayName = matricesSourceName + "_array"; { XMLWriter::Element arrayElement(writer, "float_array"); arrayElement.Attribute("id", arrayName); arrayElement.Attribute("count", skeleton.GetBoneCount() * 16); arrayElement.ContentLine(""); for (int boneIndex = 0, boneCount = skeleton.GetBoneCount(); boneIndex < boneCount; ++boneIndex) { Vec3 scaleParams; skeleton.GetScale((float*)&scaleParams, boneIndex); Matrix44 scale = Matrix33::CreateScale(scaleParams); Ang3 rotationParams; skeleton.GetRotation((float*)&rotationParams, boneIndex); Matrix44 rotation = Matrix33::CreateRotationXYZ(rotationParams); Vec3 translationParams; skeleton.GetTranslation((float*)&translationParams, boneIndex); Matrix44 translation(IDENTITY); translation.SetTranslation(translationParams); Matrix44 transform = translation * (rotation * scale); transform.Invert(); for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { arrayElement.ContentArrayElement(transform(i, j)); } arrayElement.ContentLine(""); } } } // XMLWriter::Element techniqueCommonElement(writer, "technique_common"); // XMLWriter::Element accessorElement(writer, "accessor"); accessorElement.Attribute("count", skeleton.GetBoneCount()); accessorElement.Attribute("stride", 16); accessorElement.Attribute("source", "#" + arrayName); // XMLWriter::Element paramElement(writer, "param"); paramElement.Attribute("type", std::string("float4x4")); // // // } // Read in the skinning info. SkinningData skinningData; exportSource->ReadSkinning(context, &skinningData, &modelData[controller.geometryFileIndex], controller.modelIndex, &skeleton); // Build a single array of weights. std::vector weightsArray; std::vector > weightIndexArray; int vertexCount = skinningData.GetVertexCount(); weightIndexArray.resize(vertexCount); for (int vertexIndex = 0; vertexIndex < vertexCount; ++vertexIndex) { int linkCount = skinningData.GetBoneLinkCount(vertexIndex); weightIndexArray[vertexIndex].resize(linkCount); for (int linkIndex = 0; linkIndex < linkCount; ++linkIndex) { int weightIndex = int(weightsArray.size()); weightsArray.push_back(skinningData.GetWeight(vertexIndex, linkIndex)); weightIndexArray[vertexIndex][linkIndex] = weightIndex; } } // std::string weightsSourceName = controller.name + "_weights"; { XMLWriter::Element weightsSourceElement(writer, "source"); weightsSourceElement.Attribute("id", weightsSourceName); // int weightCount = int(weightsArray.size()); std::string arrayName = weightsSourceName + "_array"; { XMLWriter::Element floatArrayElement(writer, "float_array"); floatArrayElement.Attribute("count", weightCount); floatArrayElement.Attribute("id", arrayName); for (int weightIndex = 0; weightIndex < weightCount; ++weightIndex) { floatArrayElement.ContentArrayElement(weightsArray[weightIndex]); } } // XMLWriter::Element techniqueCommonElement(writer, "technique_common"); // XMLWriter::Element accessorElement(writer, "accessor"); accessorElement.Attribute("count", weightCount); accessorElement.Attribute("stride", 1); accessorElement.Attribute("source", "#" + arrayName); // XMLWriter::Element paramElement(writer, "param"); paramElement.Attribute("type", "float"); // // // } { // XMLWriter::Element jointsElement(writer, "joints"); // { XMLWriter::Element inputElement(writer, "input"); inputElement.Attribute("semantic", "JOINT"); inputElement.Attribute("source", "#" + jointsSourceName); } // { XMLWriter::Element inputElement(writer, "input"); inputElement.Attribute("semantic", "INV_BIND_MATRIX"); inputElement.Attribute("source", "#" + matricesSourceName); } // } { // XMLWriter::Element vertexWeightsElement(writer, "vertex_weights"); int vertexCount = skinningData.GetVertexCount(); vertexWeightsElement.Attribute("count", vertexCount); // { XMLWriter::Element inputElement(writer, "input"); inputElement.Attribute("semantic", "JOINT"); inputElement.Attribute("offset", 0); inputElement.Attribute("source", "#" + jointsSourceName); } // { XMLWriter::Element inputElement(writer, "input"); inputElement.Attribute("semantic", "WEIGHT"); inputElement.Attribute("offset", 1); inputElement.Attribute("source", "#" + weightsSourceName); } // { XMLWriter::Element vcountElement(writer, "vcount"); for (int vertexIndex = 0; vertexIndex < vertexCount; ++vertexIndex) { vcountElement.ContentArrayElement(int(weightIndexArray[vertexIndex].size())); } } // { XMLWriter::Element vElement(writer, "v"); vElement.ContentLine(""); for (int vertexIndex = 0; vertexIndex < vertexCount; ++vertexIndex) { for (int linkIndex = 0, linkCount = int(weightIndexArray[vertexIndex].size()); linkIndex < linkCount; ++linkIndex) { vElement.ContentArrayElement(skinningData.GetBoneIndex(vertexIndex, linkIndex)); vElement.ContentArrayElement(weightIndexArray[vertexIndex][linkIndex]); } vElement.ContentLine(""); } // } // } // // // } // Write out the morph controllers. for (int controllerIndex = 0, controllerCount = int(morphControllers.size()); controllerIndex < controllerCount; ++controllerIndex) { MorphControllerEntry& controller = morphControllers[controllerIndex]; // Find the geometry and morphs for the model. std::map, int>::const_iterator modelGeometryMapPos = modelGeometryMap.find(std::make_pair(controller.geometryFileIndex, controller.modelIndex)); MorphDataMap::const_iterator modelMorphDataPos = morphData.find(std::make_pair(controller.geometryFileIndex, controller.modelIndex)); if (modelGeometryMapPos != modelGeometryMap.end() && modelMorphDataPos != morphData.end()) { GeometryEntry& geometry = geometries[(*modelGeometryMapPos).second]; const MorphData& modelMorphData = (*modelMorphDataPos).second; XMLWriter::Element controllerElement(writer, "controller"); controllerElement.Attribute("id", controller.name); XMLWriter::Element morphElement(writer, "morph"); morphElement.Attribute("source", "#" + geometry.name); std::string targetsSourceID = controller.name + "-source_targets"; { XMLWriter::Element sourceElement(writer, "source"); sourceElement.Attribute("id", targetsSourceID); std::string arrayID = targetsSourceID + "-array"; { XMLWriter::Element idrefArrayElement(writer, "IDREF_array"); idrefArrayElement.Attribute("id", arrayID); idrefArrayElement.Attribute("count", modelMorphData.GetMorphCount()); for (int morphIndex = 0, morphCount = modelMorphData.GetMorphCount(); morphIndex < morphCount; ++morphIndex) { // Look up the geometry for this morph. //std::vector& morphGeometries std::map, int>, int>::const_iterator morphGeometryMapPos = morphGeometryMap.find(std::make_pair(std::make_pair(controller.geometryFileIndex, controller.modelIndex), morphIndex)); int morphGeometryIndex = (morphGeometryMapPos != morphGeometryMap.end() ? (*morphGeometryMapPos).second : -1); if (morphGeometryIndex >= -1) { const MorphGeometryEntry& morphGeometry = morphGeometries[morphGeometryIndex]; idrefArrayElement.ContentArrayElement(morphGeometry.name); } } } { XMLWriter::Element techniqueCommonElement(writer, "technique_common"); XMLWriter::Element accessorElement(writer, "accessor"); accessorElement.Attribute("source", "#" + arrayID); accessorElement.Attribute("count", modelMorphData.GetMorphCount()); accessorElement.Attribute("offset", 0); accessorElement.Attribute("stride", 1); XMLWriter::Element paramElement(writer, "param"); paramElement.Attribute("name", "MORPH_TARGET"); paramElement.Attribute("type", "IDREF"); } } std::string weightsSourceID = controller.name + "-source_weights"; { XMLWriter::Element sourceElement(writer, "source"); sourceElement.Attribute("id", weightsSourceID); std::string arrayID = weightsSourceID + "-array"; { XMLWriter::Element floatArrayElement(writer, "float_array"); floatArrayElement.Attribute("id", arrayID); floatArrayElement.Attribute("count", modelMorphData.GetMorphCount()); for (int morphIndex = 0, morphCount = modelMorphData.GetMorphCount(); morphIndex < morphCount; ++morphIndex) { // Look up the geometry for this morph. //std::vector& morphGeometries std::map, int>, int>::const_iterator morphGeometryMapPos = morphGeometryMap.find(std::make_pair(std::make_pair(controller.geometryFileIndex, controller.modelIndex), morphIndex)); int morphGeometryIndex = (morphGeometryMapPos != morphGeometryMap.end() ? (*morphGeometryMapPos).second : -1); if (morphGeometryIndex >= -1) { const MorphGeometryEntry& morphGeometry = morphGeometries[morphGeometryIndex]; floatArrayElement.ContentArrayElement(0); } } } { XMLWriter::Element techniqueCommonElement(writer, "technique_common"); XMLWriter::Element accessorElement(writer, "accessor"); accessorElement.Attribute("source", "#" + arrayID); accessorElement.Attribute("count", modelMorphData.GetMorphCount()); accessorElement.Attribute("offset", 0); accessorElement.Attribute("stride", 1); XMLWriter::Element paramElement(writer, "param"); paramElement.Attribute("name", "MORPH_WEIGHT"); paramElement.Attribute("type", "float"); } } XMLWriter::Element targestElement(writer, "targets"); { XMLWriter::Element inputElement(writer, "input"); inputElement.Attribute("semantic", "MORPH_TARGET"); inputElement.Attribute("source", "#" + targetsSourceID); } { XMLWriter::Element inputElement(writer, "input"); inputElement.Attribute("semantic", "MORPH_WEIGHT"); inputElement.Attribute("source", "#" + weightsSourceID); } } } } void WriteImages(XMLWriter& writer, ProgressRange& progressRange) { XMLWriter::Element libraryImagesElement(writer, "library_images"); } void WriteMaterials(XMLWriter& writer, MaterialData& materialData, std::map& materialFXMap, std::vector& effects, std::map& materialMaterialMap, std::vector& materials, ProgressRange& progressRange) { XMLWriter::Element libraryMaterialsElement(writer, "library_materials"); for (int materialIndex = 0, materialCount = materialData.GetMaterialCount(); materialIndex < materialCount; ++materialIndex) { std::map::iterator materialMapPos = materialMaterialMap.find(materialIndex); int entryIndex = (materialMapPos != materialMaterialMap.end() ? (*materialMapPos).second : -1); std::map::iterator effectMapPos = materialFXMap.find(materialIndex); int effectIndex = (effectMapPos != materialFXMap.end() ? (*effectMapPos).second : -1); if (entryIndex >= 0) { std::string name = materials[entryIndex].name; XMLWriter::Element materialElement(writer, "material"); materialElement.Attribute("id", name); if (effectIndex >= 0) { XMLWriter::Element effectElement(writer, "instance_effect"); effectElement.Attribute("url", "#" + effects[effectIndex].name); } } } } void WriteScene(XMLWriter& writer, ProgressRange& progressRange) { XMLWriter::Element sceneElement(writer, "scene"); XMLWriter::Element instanceElement(writer, "instance_visual_scene"); instanceElement.Attribute("url", "#visual_scene_0"); } } bool ColladaWriter::Write(IExportSource* source, IExportContext* context, IXMLSink* sink, ProgressRange& progressRange) { if (FloatingPointHasPrecisionIssues()) { // If you hit this point, please change floating point settings in your VS project (and recompile it): // ConfigurationProperties -> C/C++ -> CodeGeneration -> FloatingPointModel: "/fp:strict". // Note: using "/fp:precise" doesn't help. assert(0); context->Log(ILogger::eSeverity_Error, "Cannot write Collada file, because the writer has precision issues. Contact Crytek tools programmers."); return false; } // Temporarily change the current locale so that floats get written out using periods rather than commas. LocaleChanger localeChangeToStandard(LC_NUMERIC, "C"); // Create an object to format the xml. XMLWriter writer(sink); // Export the animations to the file. { XMLWriter::Element colladaElement(writer, "COLLADA"); colladaElement.Attribute("xmlns", "http://www.collada.org/2005/11/COLLADASchema"); colladaElement.Attribute("version", "1.4.1"); // Write out the document metadata. WriteMetaData(source, writer, ProgressRange(progressRange, 0.01f)); // Read the skeleton. GeometryFileData geometryFileData; MaterialData materialData; std::vector modelData; SkeletonDataMap skeletonData; MorphDataMap morphData; { ProgressRange subProgressRange(progressRange, 0.1f); source->ReadGeometryFiles(context, &geometryFileData); bool ok = source->ReadMaterials(context, &geometryFileData, &materialData); if (!ok) { return false; } modelData.resize(geometryFileData.GetGeometryFileCount()); for (int geometryFileIndex = 0, geometryFileCount = geometryFileData.GetGeometryFileCount(); geometryFileIndex < geometryFileCount; ++geometryFileIndex) { source->ReadModels(&geometryFileData, geometryFileIndex, &modelData[geometryFileIndex]); for (int modelIndex = 0, modelCount = modelData[geometryFileIndex].GetModelCount(); modelIndex < modelCount; ++modelIndex) { MorphDataMap::iterator morphDataPos = morphData.insert(std::make_pair(std::make_pair(geometryFileIndex, modelIndex), MorphData())).first; source->ReadMorphs(context, &(*morphDataPos).second, &modelData[geometryFileIndex], modelIndex); if ((*morphDataPos).second.GetMorphCount() == 0) { morphData.erase(morphDataPos); } SkeletonDataMap::iterator skeletonDataPos = skeletonData.insert(std::make_pair(std::make_pair(geometryFileIndex, modelIndex), SkeletonData())).first; if (!source->ReadSkeleton(&geometryFileData, geometryFileIndex, &modelData[geometryFileIndex], modelIndex, &materialData, &(*skeletonDataPos).second)) { skeletonData.erase(skeletonDataPos); } #if !defined(HACK_HACK_FORCE_PELVIS_TO_BE_BONE_1_BECAUSE_LOADING_CODE_EXPECTS_IT) # define HACK_HACK_FORCE_PELVIS_TO_BE_BONE_1_BECAUSE_LOADING_CODE_EXPECTS_IT 0 #endif //!defined(HACK_HACK_FORCE_PELVIS_TO_BE_BONE_1_BECAUSE_LOADING_CODE_EXPECTS_IT) skeletonDataPos = skeletonData.find(std::make_pair(geometryFileIndex, modelIndex)); if (skeletonDataPos != skeletonData.end()) { SkeletonData newData, & oldData = (*skeletonDataPos).second; int pelvisIndex = -1; for (int i = 0, count = oldData.GetBoneCount(); i < count; ++i) { pelvisIndex = ((_stricmp(oldData.GetName(i).c_str(), "Bip01 Pelvis") == 0) ? i : pelvisIndex); } if (pelvisIndex >= 0) { if (pelvisIndex != 1) { context->Log(ILogger::eSeverity_Warning, "`Bip01 Pelvis` should be the second bone."); } #if HACK_HACK_FORCE_PELVIS_TO_BE_BONE_1_BECAUSE_LOADING_CODE_EXPECTS_IT == 1 std::vector oldToNewMap(oldData.GetBoneCount()); std::vector newToOldMap(oldData.GetBoneCount()); for (int i = 0, count = oldData.GetBoneCount(); i < count; ++i) { oldToNewMap[i] = i, newToOldMap[i] = i; } std::swap(oldToNewMap[pelvisIndex], oldToNewMap[1]); std::swap(newToOldMap[pelvisIndex], newToOldMap[1]); for (int i = 0, count = oldData.GetBoneCount(); i < count; ++i) { int oldIndex = newToOldMap[i]; void* handle = oldData.GetBoneHandle(oldIndex); std::string name = oldData.GetName(oldIndex); int oldParentIndex = oldData.GetParentIndex(oldIndex); int parentIndex = (oldParentIndex >= 0 ? oldToNewMap[oldParentIndex] : -1); float translation[3], rotation[3], scale[3]; oldData.GetTranslation(translation, oldIndex); oldData.GetRotation(rotation, oldIndex); oldData.GetScale(scale, oldIndex); int boneIndex = newData.AddBone(handle, name.c_str(), parentIndex); newData.SetTranslation(boneIndex, translation); newData.SetRotation(boneIndex, rotation); newData.SetScale(boneIndex, scale); newData.SetHasGeometry(boneIndex, oldData.HasGeometry(oldIndex)); if (oldData.HasParentFrame(oldIndex)) { float parentFrameTranslation[3], parentFrameRotation[3], parentFrameScale[3]; oldData.GetParentFrameTranslation(oldIndex, parentFrameTranslation); oldData.GetParentFrameRotation(oldIndex, parentFrameRotation); oldData.GetParentFrameScale(oldIndex, parentFrameScale); newData.SetParentFrameTranslation(boneIndex, parentFrameTranslation); newData.SetParentFrameRotation(boneIndex, parentFrameRotation); newData.SetParentFrameScale(boneIndex, parentFrameScale); } for (int axisIndex = 0; axisIndex < 3; ++axisIndex) { ISkeletonData::Axis axis = ISkeletonData::Axis(axisIndex); if (oldData.HasLimit(oldIndex, axis, ISkeletonData::LimitMin)) { newData.SetLimit(boneIndex, axis, ISkeletonData::LimitMin, oldData.GetLimit(oldIndex, axis, ISkeletonData::LimitMin)); } if (oldData.HasLimit(oldIndex, axis, ISkeletonData::LimitMax)) { newData.SetLimit(boneIndex, axis, ISkeletonData::LimitMax, oldData.GetLimit(oldIndex, axis, ISkeletonData::LimitMax)); } if (oldData.HasSpringTension(oldIndex, axis)) { newData.SetSpringTension(boneIndex, axis, oldData.GetSpringTension(oldIndex, axis)); } if (oldData.HasSpringAngle(oldIndex, axis)) { newData.SetSpringAngle(boneIndex, axis, oldData.GetSpringAngle(oldIndex, axis)); } if (oldData.HasAxisDamping(oldIndex, axis)) { newData.SetAxisDamping(boneIndex, axis, oldData.GetAxisDamping(oldIndex, axis)); } newData.SetPhysicalized(boneIndex, oldData.GetPhysicalized(oldIndex)); } } (*skeletonDataPos).second = newData; #endif //HACK_HACK_FORCE_PELVIS_TO_BE_BONE_1_BECAUSE_LOADING_CODE_EXPECTS_IT == 1 } } } } } // Generate a list of fx to export. std::map materialFXMap; std::vector effects; GenerateEffectsList(context, materialFXMap, effects, materialData); // Generate a list of geometry to export. std::map, int> modelGeometryMap; std::vector geometries; GenerateGeometryList(context, modelGeometryMap, geometries, geometryFileData, modelData); // Generate a list of bone geometries to export. std::map, int>, int> boneGeometryMap; std::vector boneGeometries; GenerateBoneGeometryList(context, boneGeometryMap, boneGeometries, geometryFileData, modelData, skeletonData); // Generate a list of morph geometries to export. std::map, int>, int> morphGeometryMap; std::vector morphGeometries; GenerateMorphGeometryList(context, morphGeometryMap, morphGeometries, geometryFileData, modelData, morphData); BoneDataMap boneDataMap; GenerateBoneList(context, boneDataMap, skeletonData, modelData); // Generate a list of animations to export. std::vector animations; GenerateAnimationList(context, animations, geometryFileData, modelData, skeletonData, source, ProgressRange(progressRange, 0.025f)); // Generate a list of morph controllers to export. std::vector morphControllers; std::map, int> modelMorphControllerMap; GenerateMorphControllerList(context, morphControllers, modelMorphControllerMap, morphData, geometryFileData, modelData, modelGeometryMap, geometries, ProgressRange(progressRange, 0.0125f)); // Generate a list of skin controllers to export. std::vector controllers; std::map, int> modelControllerMap; GenerateSkinControllerList(context, controllers, modelControllerMap, skeletonData, geometryFileData, modelData, modelGeometryMap, geometries, ProgressRange(progressRange, 0.0125f)); // Write out all the animations. WriteAnimationList(writer, animations, ProgressRange(progressRange, 0.025f)); WriteAnimationData(context, writer, animations, geometryFileData, modelData, skeletonData, boneDataMap, source, ProgressRange(progressRange, 0.475f)); // Write out all the effects. WriteEffects(writer, effects, ProgressRange(progressRange, 0.01f)); // Write out the materials. std::map materialMaterialMap; std::vector materials; GenerateMaterialList(context, materialMaterialMap, materialFXMap, effects, materials, materialData); WriteMaterials(writer, materialData, materialFXMap, effects, materialMaterialMap, materials, ProgressRange(progressRange, 0.005f)); // Write out all the geometries. bool ok = WriteGeometries(context, writer, geometries, geometryFileData, modelData, morphData, materialData, materials, materialMaterialMap, skeletonData, boneGeometries, boneGeometryMap, morphGeometryMap, morphGeometries, source, ProgressRange(progressRange, 0.2f)); if (!ok) { return false; } // Write out all the controllers. WriteControllers(writer, context, source, controllers, morphControllers, modelMorphControllerMap, geometryFileData, modelData, skeletonData, morphData, morphGeometries, morphGeometryMap, geometries, modelGeometryMap, boneDataMap, ProgressRange(progressRange, 0.005f)); // Write out the list of models. WriteHierarchy(writer, context, geometryFileData, materialData, materialMaterialMap, materials, modelData, skeletonData, modelGeometryMap, geometries, modelControllerMap, controllers, boneDataMap, boneGeometryMap, boneGeometries, modelMorphControllerMap, morphControllers, source, ProgressRange(progressRange, 0.1f)); // Write out all the other libraries. WriteImages(writer, ProgressRange(progressRange, 0.01f)); WriteScene(writer, ProgressRange(progressRange, 0.01f)); } return true; }