You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/Tools/CryCommonTools/Export/ColladaWriter.cpp

2919 lines
147 KiB
C++

/*
* 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 <cstdio>
#include <ctime>
#include <cctype>
#include <locale.h>
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<std::pair<int, int>, SkeletonData> SkeletonDataMap;
typedef std::map<std::pair<int, int>, MorphData> MorphDataMap;
typedef std::map<std::pair<int, int>, std::vector<BoneEntry> > 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<std::pair<int, int>, MorphControllerEntry> MorphControllerMap;
void BindMaterials(XMLWriter& writer, IExportContext* context, MaterialData& materialData, const ModelData& modelData, int modelIndex, const std::map<int, int>& materialMaterialMap, const std::vector<MaterialEntry>& materials, IExportSource* source)
{
// Instance any materials that the node uses.
GeometryMaterialData geometryMaterialData;
source->ReadGeometryMaterialData(context, &geometryMaterialData, &modelData, &materialData, modelIndex);
std::set<int> 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<int>::const_iterator usedMtlPos = usedMaterialIndices.begin(), usedMtlEnd = usedMaterialIndices.end(); usedMtlPos != usedMtlEnd; ++usedMtlPos)
{
std::map<int, int>::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<int, int>& materialMaterialMap, const std::vector<MaterialEntry>& materials, IExportSource* source)
{
// Instance any materials that the node uses.
GeometryMaterialData geometryMaterialData;
source->ReadBoneGeometryMaterialData(context, &geometryMaterialData, &skeletonData, boneIndex, &materialData);
std::set<int> 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<int>::const_iterator usedMtlPos = usedMaterialIndices.begin(), usedMtlEnd = usedMaterialIndices.end(); usedMtlPos != usedMtlEnd; ++usedMtlPos)
{
std::map<int, int>::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<int> 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<int, int>::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<BoneEntry>& bones, std::map<std::pair<std::pair<int, int>, int>, int>& boneGeometryMap, std::vector<BoneGeometryEntry>& boneGeometries, int geometryFileIndex, int modelIndex, MaterialData& materialData, const std::map<int, int>& materialMaterialMap, const std::vector<MaterialEntry>& 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<std::pair<std::pair<int, int>, 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<BoneEntry>& 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<int, int>& materialMaterialMap, std::vector<MaterialEntry>& 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<std::vector<GeometryData::Polygon> > 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<int, int>::iterator materialMapPos = materialMaterialMap.find(materialIndex);
int materialEntryIndex = (materialMapPos != materialMaterialMap.end() ? (*materialMapPos).second : -1);
std::vector<GeometryData::Polygon>& 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<GeometryEntry>& geometries, GeometryFileData& geometryFileData, const std::vector<ModelData>& modelData, MorphDataMap& morphData, MaterialData& materialData, std::vector<MaterialEntry>& materials, std::map<int, int>& materialMaterialMap, SkeletonDataMap& skeletonData, std::vector<BoneGeometryEntry>& boneGeometries, std::map<std::pair<std::pair<int, int>, int>, int>& boneGeometryMap, std::map<std::pair<std::pair<int, int>, int>, int>& morphGeometryMap, std::vector<MorphGeometryEntry>& 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<int, int>& materialMaterialMap,
const std::vector<MaterialEntry>& materials,
const ModelData& modelData,
int const modelIndex,
std::map<std::pair<int, int>, int>& modelGeometryMap,
std::vector<GeometryEntry>& geometries,
std::map<std::pair<int, int>, int>& modelControllerMap,
std::vector<SkinControllerEntry>& controllers,
std::map<std::pair<int, int>, int>& modelMorphControllerMap,
std::vector<MorphControllerEntry>& 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<std::pair<int, int>, int>::iterator modelControllerMapPos = modelControllerMap.find(std::make_pair(geometryFileIndex, modelIndex));
std::map<std::pair<int, int>, 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<std::pair<int, int>, 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<int, int>& materialMaterialMap,
const std::vector<MaterialEntry>& materials,
const std::vector<ModelData>& modelData,
SkeletonDataMap& skeletonData,
std::map<std::pair<int, int>, int>& modelGeometryMap,
std::vector<GeometryEntry>& geometries,
std::map<std::pair<int, int>, int>& modelControllerMap,
std::vector<SkinControllerEntry>& controllers,
BoneDataMap& boneDataMap,
std::map<std::pair<std::pair<int, int>, int>, int>& boneGeometryMap,
std::vector<BoneGeometryEntry>& boneGeometries,
std::map<std::pair<int, int>, int>& modelMorphControllerMap,
std::vector<MorphControllerEntry>& 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<BoneEntry>& 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<int>(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<AnimationBoneParameterEntry> 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<AnimationEntry>& 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<AnimationEntry>& animations, GeometryFileData& geometryFileData, const std::vector<ModelData>& 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<SkinControllerEntry>& controllers, std::map<std::pair<int, int>, int>& modelControllerMap, SkeletonDataMap& skeletonData, GeometryFileData& geometryFileData, const std::vector<ModelData>& modelData, std::map<std::pair<int, int>, int>& modelGeometryMap, std::vector<GeometryEntry>& 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<std::pair<int, int>, 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<MorphControllerEntry>& morphControllers, std::map<std::pair<int, int>, int>& modelMorphControllerMap, MorphDataMap& morphData, GeometryFileData& geometryFileData, const std::vector<ModelData>& modelData, std::map<std::pair<int, int>, int>& modelGeometryMap, std::vector<GeometryEntry>& 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<std::pair<int, int>, 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<int, int>& materialFXMap, std::vector<EffectsEntry>& 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<int, int>& materialMaterialMap, std::map<int, int>& materialFXMap, std::vector<EffectsEntry>& effects, std::vector<MaterialEntry>& 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: <Library>__<ID>__<Name>[__<param>...]
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<std::pair<int, int>, int>& modelGeometryMap, std::vector<GeometryEntry>& geometries, GeometryFileData& geometryFileData, const std::vector<ModelData>& 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<std::pair<std::pair<int, int>, int>, int>& boneGeometryMap, std::vector<BoneGeometryEntry>& boneGeometries, GeometryFileData& geometryFileData, const std::vector<ModelData>& 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<std::pair<std::pair<int, int>, int>, int>& morphGeometryMap, std::vector<MorphGeometryEntry>& morphGeometries, GeometryFileData& geometryFileData, const std::vector<ModelData>& 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<std::pair<std::string, std::string> >& 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>& 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<BoneEntry>())).first;
std::vector<BoneEntry>& 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<std::pair<std::string, std::string> > 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<AnimationEntry>& 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<BoneEntry>* 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<AnimationEntry>& animations,
GeometryFileData& geometryFileData,
const std::vector<ModelData>& 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<BoneEntry>& bones = (*boneDataPos).second;
if (animationData)
{
WriteAnimationTags(animationEntryProgressRange, entry, animationData, writer, &bones, NULL);
delete animationData;
}
}
}
}
}
void WriteEffects(XMLWriter& writer, std::vector<EffectsEntry>& 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<SkinControllerEntry>& skinControllers,
std::vector<MorphControllerEntry>& morphControllers,
std::map<std::pair<int, int>, int> modelMorphControllerMap,
GeometryFileData& geometryFileData,
const std::vector<ModelData>& modelData,
SkeletonDataMap& skeletonData,
MorphDataMap& morphData,
std::vector<MorphGeometryEntry>& morphGeometries,
std::map<std::pair<std::pair<int, int>, int>, int>& morphGeometryMap,
std::vector<GeometryEntry>& geometries,
std::map<std::pair<int, int>, int>& modelGeometryMap,
const BoneDataMap& boneDataMap,
ProgressRange& progressRange)
{
//<library_controllers>
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;
// <controller id="controllers_0">
XMLWriter::Element controllerElement(writer, "controller");
controllerElement.Attribute("id", controller.name);
// <skin source="#geometries_60">
std::string geometryName;
bool sourceFound = false;
{
std::map<std::pair<int, int>, 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<std::pair<int, int>, 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);
// <bind_shape_matrix>
// 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
// </bind_shape_matrix>
{
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);
}
// <source id="controllers_0-Joints">
std::string jointsSourceName = controller.name + "_joints";
{
XMLWriter::Element jointsSourceElement(writer, "source");
jointsSourceElement.Attribute("id", jointsSourceName);
// <IDREF_array count="61" id="controllers_0-Joints-array"></IDREF_array>
const std::vector<BoneEntry>& 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);
}
}
// <technique_common>
{
XMLWriter::Element techniqueCommonElement(writer, "technique_common");
// <accessor count="61" stride="1" source="#controllers_0-Joints-array">
XMLWriter::Element accessorElement(writer, "accessor");
accessorElement.Attribute("count", skeleton.GetBoneCount());
accessorElement.Attribute("stride", 1);
accessorElement.Attribute("source", "#" + arrayName);
// <param type="IDREF"/>
XMLWriter::Element paramElement(writer, "param");
paramElement.Attribute("type", std::string("IDREF"));
// </accessor>
// </technique_common>
// </source>
}
}
// <source id="controllers_0-Matrices">
std::string matricesSourceName = controller.name + "_matrices";
{
XMLWriter::Element sourceElement(writer, "source");
sourceElement.Attribute("id", matricesSourceName);
// <float_array count="976" id="controllers_0-Matrices-array"></float_array>
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("");
}
}
}
// <technique_common>
XMLWriter::Element techniqueCommonElement(writer, "technique_common");
// <accessor count="61" stride="16" source="#controllers_0-Matrices-array">
XMLWriter::Element accessorElement(writer, "accessor");
accessorElement.Attribute("count", skeleton.GetBoneCount());
accessorElement.Attribute("stride", 16);
accessorElement.Attribute("source", "#" + arrayName);
// <param type="float4x4"/>
XMLWriter::Element paramElement(writer, "param");
paramElement.Attribute("type", std::string("float4x4"));
// </accessor>
// </technique_common>
// </source>
}
// 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<float> weightsArray;
std::vector<std::vector<int> > 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;
}
}
// <source id="controllers_0-Weights">
std::string weightsSourceName = controller.name + "_weights";
{
XMLWriter::Element weightsSourceElement(writer, "source");
weightsSourceElement.Attribute("id", weightsSourceName);
// <float_array count="3957" id="controllers_0-Weights-array"></float_array>
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]);
}
}
// <technique_common>
XMLWriter::Element techniqueCommonElement(writer, "technique_common");
// <accessor count="3957" stride="1" source="#controllers_0-Weights-array">
XMLWriter::Element accessorElement(writer, "accessor");
accessorElement.Attribute("count", weightCount);
accessorElement.Attribute("stride", 1);
accessorElement.Attribute("source", "#" + arrayName);
// <param type="float"/>
XMLWriter::Element paramElement(writer, "param");
paramElement.Attribute("type", "float");
// </accessor>
// </technique_common>
// </source>
}
{
// <joints>
XMLWriter::Element jointsElement(writer, "joints");
// <input semantic="JOINT" source="#controllers_0-Joints"></input >
{
XMLWriter::Element inputElement(writer, "input");
inputElement.Attribute("semantic", "JOINT");
inputElement.Attribute("source", "#" + jointsSourceName);
}
// <input semantic="INV_BIND_MATRIX" source="#controllers_0-Matrices"></input >
{
XMLWriter::Element inputElement(writer, "input");
inputElement.Attribute("semantic", "INV_BIND_MATRIX");
inputElement.Attribute("source", "#" + matricesSourceName);
}
// </joints>
}
{
// <vertex_weights count="2157">
XMLWriter::Element vertexWeightsElement(writer, "vertex_weights");
int vertexCount = skinningData.GetVertexCount();
vertexWeightsElement.Attribute("count", vertexCount);
// <input semantic="JOINT" offset="0" source="#controllers_0-Joints"></input >
{
XMLWriter::Element inputElement(writer, "input");
inputElement.Attribute("semantic", "JOINT");
inputElement.Attribute("offset", 0);
inputElement.Attribute("source", "#" + jointsSourceName);
}
// <input semantic="WEIGHT" offset="1" source="#controllers_0-Weights"></input >
{
XMLWriter::Element inputElement(writer, "input");
inputElement.Attribute("semantic", "WEIGHT");
inputElement.Attribute("offset", 1);
inputElement.Attribute("source", "#" + weightsSourceName);
}
// <vcount></vcount>
{
XMLWriter::Element vcountElement(writer, "vcount");
for (int vertexIndex = 0; vertexIndex < vertexCount; ++vertexIndex)
{
vcountElement.ContentArrayElement(int(weightIndexArray[vertexIndex].size()));
}
}
// <v>
{
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("");
}
// </v>
}
// </vertex_weights>
}
// </skin>
// </controller>
//</library_controllers>
}
// 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<std::pair<int, int>, 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<MorphGeometryEntry>& morphGeometries
std::map<std::pair<std::pair<int, int>, 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<MorphGeometryEntry>& morphGeometries
std::map<std::pair<std::pair<int, int>, 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<int, int>& materialFXMap, std::vector<EffectsEntry>& effects, std::map<int, int>& materialMaterialMap, std::vector<MaterialEntry>& materials, ProgressRange& progressRange)
{
XMLWriter::Element libraryMaterialsElement(writer, "library_materials");
for (int materialIndex = 0, materialCount = materialData.GetMaterialCount(); materialIndex < materialCount; ++materialIndex)
{
std::map<int, int>::iterator materialMapPos = materialMaterialMap.find(materialIndex);
int entryIndex = (materialMapPos != materialMaterialMap.end() ? (*materialMapPos).second : -1);
std::map<int, int>::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> 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<int> oldToNewMap(oldData.GetBoneCount());
std::vector<int> 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<int, int> materialFXMap;
std::vector<EffectsEntry> effects;
GenerateEffectsList(context, materialFXMap, effects, materialData);
// Generate a list of geometry to export.
std::map<std::pair<int, int>, int> modelGeometryMap;
std::vector<GeometryEntry> geometries;
GenerateGeometryList(context, modelGeometryMap, geometries, geometryFileData, modelData);
// Generate a list of bone geometries to export.
std::map<std::pair<std::pair<int, int>, int>, int> boneGeometryMap;
std::vector<BoneGeometryEntry> boneGeometries;
GenerateBoneGeometryList(context, boneGeometryMap, boneGeometries, geometryFileData, modelData, skeletonData);
// Generate a list of morph geometries to export.
std::map<std::pair<std::pair<int, int>, int>, int> morphGeometryMap;
std::vector<MorphGeometryEntry> morphGeometries;
GenerateMorphGeometryList(context, morphGeometryMap, morphGeometries, geometryFileData, modelData, morphData);
BoneDataMap boneDataMap;
GenerateBoneList(context, boneDataMap, skeletonData, modelData);
// Generate a list of animations to export.
std::vector<AnimationEntry> animations;
GenerateAnimationList(context, animations, geometryFileData, modelData, skeletonData, source, ProgressRange(progressRange, 0.025f));
// Generate a list of morph controllers to export.
std::vector<MorphControllerEntry> morphControllers;
std::map<std::pair<int, int>, 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<SkinControllerEntry> controllers;
std::map<std::pair<int, int>, 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<int, int> materialMaterialMap;
std::vector<MaterialEntry> 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;
}