Kawe Mazidjatari 04bee896be Fix string/wstring type conflict
cppkore uses string/wstring as StringBase while we use std::string/std::wstring as string/wstring. Changed all types in cppkore to String/WString instead.
2022-05-21 21:51:35 +02:00

603 lines
19 KiB
C++

#include "stdafx.h"
#include "KaydaraFBX.h"
#include "KaydaraFBXContainer.h"
#include "File.h"
#include "Matrix.h"
#include "Path.h"
#include "BinaryWriter.h"
namespace Assets::Exporters
{
void AddObjectConnection(KaydaraFBXNode& ConnectionsNode, uint64_t From, uint64_t To)
{
auto& Connection = ConnectionsNode.Children.Emplace("C");
Connection.AddPropertyString("OO");
Connection.AddPropertyInteger64(From);
Connection.AddPropertyInteger64(To);
}
void AddObjectPropertyConnection(KaydaraFBXNode& ConnectionsNode, uint64_t From, uint64_t To, const char* PropertyName)
{
auto& Connection = ConnectionsNode.Children.Emplace("C");
Connection.AddPropertyString("OP");
Connection.AddPropertyInteger64(From);
Connection.AddPropertyInteger64(To);
Connection.AddPropertyString(PropertyName);
}
void InitializeRootNode(KaydaraFBXNode& RootNode)
{
RootNode.Children.Emplace("Version").AddPropertyInteger32(232);
auto& Properties = RootNode.Children.Emplace("Properties70");
{
auto& Prop = Properties.Children.Emplace("P");
Prop.AddPropertyString("Lcl Rotation");
Prop.AddPropertyString("Lcl Rotation");
Prop.AddPropertyString("");
Prop.AddPropertyString("A");
Prop.AddPropertyFloat64(-90); // To match default Maya axis
Prop.AddPropertyFloat64(0);
Prop.AddPropertyFloat64(0);
}
}
void InitializeMaterialTexture(KaydaraFBXNode& ObjectsNode, Material& Material, uint64_t TextureId)
{
auto& Texture = ObjectsNode.Children.Emplace("Texture");
auto TextureName = Material.Name + "_c" + String("\u0000\u0001Texture", 9);
Texture.AddPropertyInteger64(TextureId);
Texture.AddPropertyString(TextureName);
Texture.AddPropertyString("");
Texture.Children.Emplace("Type").AddPropertyString("TextureVideoClip");
Texture.Children.Emplace("Version").AddPropertyInteger32(202);
Texture.Children.Emplace("TextureName").AddPropertyString(TextureName);
auto& Properties = Texture.Children.Emplace("Properties70");
{
auto& Prop = Properties.Children.Emplace("P");
Prop.AddPropertyString("CurrentTextureBlendMode");
Prop.AddPropertyString("enum");
Prop.AddPropertyString("");
Prop.AddPropertyString("");
Prop.AddPropertyInteger32(0);
}
{
auto& Prop = Properties.Children.Emplace("P");
Prop.AddPropertyString("UVSet");
Prop.AddPropertyString("KString");
Prop.AddPropertyString("");
Prop.AddPropertyString("");
Prop.AddPropertyString("map1");
}
{
auto& Prop = Properties.Children.Emplace("P");
Prop.AddPropertyString("UseMaterial");
Prop.AddPropertyString("bool");
Prop.AddPropertyString("");
Prop.AddPropertyString("");
Prop.AddPropertyInteger32(1);
}
Texture.Children.Emplace("Media").AddPropertyString(Material.Name + "_c" + String("\u0000\u0001Video", 7));
String DiffuseMap = "";
if (Material.Slots.ContainsKey(MaterialSlotType::Albedo))
DiffuseMap = Material.Slots[MaterialSlotType::Albedo].first;
else if (Material.Slots.ContainsKey(MaterialSlotType::Diffuse))
DiffuseMap = Material.Slots[MaterialSlotType::Diffuse].first;
Texture.Children.Emplace("FileName").AddPropertyString(DiffuseMap.Replace("\\", "/"));
Texture.Children.Emplace("RelativeFilename").AddPropertyString(DiffuseMap);
}
void InitializeMeshSkinCluster(KaydaraFBXNode& ObjectsNode, KaydaraFBXNode& ConnectionsNode, uint64_t SubmeshIndex, uint64_t GeometryId, uint64_t DeformerId)
{
auto& SkinDeformer = ObjectsNode.Children.Emplace("Deformer");
SkinDeformer.AddPropertyInteger64(DeformerId);
SkinDeformer.AddPropertyString(String::Format("KoreLibMesh%02d", SubmeshIndex) + String("\u0000\u0001Deformer", 10));
SkinDeformer.AddPropertyString("Skin");
SkinDeformer.Children.Emplace("Version").AddPropertyInteger32(101);
SkinDeformer.Children.Emplace("Link_DeformAcuracy").AddPropertyFloat64(50);
AddObjectConnection(ConnectionsNode, DeformerId, GeometryId);
}
void InitializeMeshSubDeformer(KaydaraFBXNode& ObjectsNode, Bone& Bone, uint64_t SubmeshIndex, uint64_t BoneIndex, uint64_t DeformerId)
{
auto& SubDeformer = ObjectsNode.Children.Emplace("Deformer");
SubDeformer.AddPropertyInteger64(DeformerId);
SubDeformer.AddPropertyString(String::Format("KoreLibMesh%02d_Bone%02d", SubmeshIndex, BoneIndex) + String("\u0000\u0001SubDeformer", 13));
SubDeformer.AddPropertyString("Cluster");
SubDeformer.Children.Emplace("Version").AddPropertyInteger32(100);
auto GlobalMatrix = Math::Matrix::CreateFromQuaternion(Bone.GlobalRotation());
GlobalMatrix.SetPosition(Bone.GlobalPosition());
auto InverseGlobalMatrix = GlobalMatrix.Inverse();
SubDeformer.Children.Emplace("Indexes").Properties.EmplaceBack('i', nullptr, 0);
SubDeformer.Children.Emplace("Weights").Properties.EmplaceBack('d', nullptr, 0);
auto& Transform = SubDeformer.Children.Emplace("Transform").Properties.Emplace('d', nullptr, 0);
auto& TransformLink = SubDeformer.Children.Emplace("TransformLink").Properties.Emplace('d', nullptr, 0);
for (uint32_t i = 0; i < 16; i++)
{
Transform.AddValueFloat64(InverseGlobalMatrix.GetMatrix()[i]);
TransformLink.AddValueFloat64(GlobalMatrix.GetMatrix()[i]);
}
}
bool KaydaraFBX::ExportAnimation(const Animation& Animation, const String& Path)
{
return false;
}
bool KaydaraFBX::ExportModel(const Model& Model, const String& Path)
{
auto Writer = IO::BinaryWriter(IO::File::Create(Path));
auto FileName = IO::Path::GetFileNameWithoutExtension(Path);
KaydaraFBXDocument Document;
auto& ObjectsNode = Document.GetRootNode("Objects");
auto& ConnectionsNode = Document.GetRootNode("Connections");
uint32_t SubmeshIndex = 0;
uint32_t BoneIndex = 0;
uint32_t MatIndex = 0;
uint64_t UniqueId = 0xDEADC0DE;
uint64_t ModelId = 0x10000001;
uint64_t DeformerId = 0x80000008;
uint64_t RootModelId = 0xC0DEDEAD;
uint64_t RootJointsId = 0xC0DEBEAD;
auto& RootModelNode = ObjectsNode.Children.Emplace("Model");
auto& RootJointsNode = ObjectsNode.Children.Emplace("Model");
RootModelNode.AddPropertyInteger64(RootModelId);
RootModelNode.AddPropertyString(FileName + String("\u0000\u0001Model", 7));
RootModelNode.AddPropertyString("Null");
RootJointsNode.AddPropertyInteger64(RootJointsId);
RootJointsNode.AddPropertyString("Joints\u0000\u0001Model", 13);
RootJointsNode.AddPropertyString("Null");
InitializeRootNode(RootModelNode);
InitializeRootNode(RootJointsNode);
Dictionary<int32_t, uint64_t> BoneIdMap;
for (auto& Bone : Model.Bones)
{
auto& SkeletonNode = ObjectsNode.Children.Emplace("NodeAttribute");
SkeletonNode.AddPropertyInteger64(ModelId);
SkeletonNode.AddPropertyString("\u0000\u0001NodeAttribute", 15);
SkeletonNode.AddPropertyString("LimbNode");
auto& SkeletonNodeProps = SkeletonNode.Children.Emplace("Properties70");
{
auto& Prop = SkeletonNodeProps.Children.Emplace("P");
Prop.AddPropertyString("Size");
Prop.AddPropertyString("double");
Prop.AddPropertyString("Number");
Prop.AddPropertyString("");
Prop.AddPropertyFloat64(16.5);
}
SkeletonNode.Children.Emplace("TypeFlags").AddPropertyString("Skeleton");
auto& JointModelNode = ObjectsNode.Children.Emplace("Model");
auto& Position = Bone.LocalPosition();
auto Rotation = Bone.LocalRotation().ToEulerAngles();
JointModelNode.AddPropertyInteger64(UniqueId);
JointModelNode.AddPropertyString(Bone.Name() + String("\u0000\u0001Model", 7));
JointModelNode.AddPropertyString("LimbNode");
JointModelNode.Children.Emplace("Version").AddPropertyInteger32(232);
auto & JointNodeProps = JointModelNode.Children.Emplace("Properties70");
{
auto& Prop = JointNodeProps.Children.Emplace("P");
Prop.AddPropertyString("PreRotation");
Prop.AddPropertyString("Vector3D");
Prop.AddPropertyString("Vector");
Prop.AddPropertyString("");
Prop.AddPropertyFloat64(Rotation.X);
Prop.AddPropertyFloat64(Rotation.Y);
Prop.AddPropertyFloat64(Rotation.Z);
}
{
auto& Prop = JointNodeProps.Children.Emplace("P");
Prop.AddPropertyString("RotationActive");
Prop.AddPropertyString("bool");
Prop.AddPropertyString("");
Prop.AddPropertyString("");
Prop.AddPropertyInteger32(1);
}
{
auto& Prop = JointNodeProps.Children.Emplace("P");
Prop.AddPropertyString("InheritType");
Prop.AddPropertyString("enum");
Prop.AddPropertyString("");
Prop.AddPropertyString("");
Prop.AddPropertyInteger32(2);
}
{
auto& Prop = JointNodeProps.Children.Emplace("P");
Prop.AddPropertyString("ScalingMax");
Prop.AddPropertyString("Vector3D");
Prop.AddPropertyString("Vector");
Prop.AddPropertyString("");
Prop.AddPropertyFloat64(0);
Prop.AddPropertyFloat64(0);
Prop.AddPropertyFloat64(0);
}
{
auto& Prop = JointNodeProps.Children.Emplace("P");
Prop.AddPropertyString("DefaultAttributeIndex");
Prop.AddPropertyString("int");
Prop.AddPropertyString("Integer");
Prop.AddPropertyString("");
Prop.AddPropertyInteger32(0);
}
{
auto& Prop = JointNodeProps.Children.Emplace("P");
Prop.AddPropertyString("Lcl Translation");
Prop.AddPropertyString("Lcl Translation");
Prop.AddPropertyString("");
Prop.AddPropertyString("A");
Prop.AddPropertyFloat64(Position.X);
Prop.AddPropertyFloat64(Position.Y);
Prop.AddPropertyFloat64(Position.Z);
}
AddObjectConnection(ConnectionsNode, ModelId, UniqueId);
auto ParentIndex = Bone.Parent();
if (ParentIndex >= 0)
{
AddObjectConnection(ConnectionsNode, UniqueId, BoneIdMap[ParentIndex]);
}
else
{
AddObjectConnection(ConnectionsNode, UniqueId, RootJointsId);
}
BoneIdMap.Add(BoneIndex, UniqueId);
UniqueId++;
ModelId++;
BoneIndex++;
}
Dictionary<int32_t, uint64_t> MatIdMap;
for (auto& Mat : Model.Materials)
{
auto& MaterialNode = ObjectsNode.Children.Emplace("Material");
MaterialNode.AddPropertyInteger64(UniqueId);
MaterialNode.AddPropertyString(Mat.Name + String("\u0000\u0001Material", 10));
MaterialNode.AddPropertyString("");
MaterialNode.Children.Emplace("Version").AddPropertyInteger32(102);
MaterialNode.Children.Emplace("ShadingModel").AddPropertyString("Lambert");
MaterialNode.Children.Emplace("MultiLayer").AddPropertyInteger32(0);
MatIdMap.Add(MatIndex, UniqueId);
UniqueId++;
MatIndex++;
String DiffuseMap = "";
if (Mat.Slots.ContainsKey(MaterialSlotType::Albedo))
DiffuseMap = Mat.Slots[MaterialSlotType::Albedo].first;
else if (Mat.Slots.ContainsKey(MaterialSlotType::Diffuse))
DiffuseMap = Mat.Slots[MaterialSlotType::Diffuse].first;
if (!String::IsNullOrWhiteSpace(DiffuseMap))
{
InitializeMaterialTexture(ObjectsNode, Mat, UniqueId);
AddObjectPropertyConnection(ConnectionsNode, UniqueId, UniqueId - 1, "DiffuseColor");
UniqueId++;
}
}
for (auto& Mesh : Model.Meshes)
{
auto& MeshModelNode = ObjectsNode.Children.Emplace("Model");
MeshModelNode.AddPropertyInteger64(ModelId);
MeshModelNode.AddPropertyString(String::Format("KoreLibMesh%02d", SubmeshIndex) + String("\u0000\u0001Model", 7));
MeshModelNode.AddPropertyString("Mesh");
MeshModelNode.Children.Emplace("Version").AddPropertyInteger32(232);
auto& MeshNodeProps = MeshModelNode.Children.Emplace("Properties70");
{
auto& MeshProp = MeshNodeProps.Children.Emplace("P");
MeshProp.AddPropertyString("Lcl Rotation");
MeshProp.AddPropertyString("Lcl Rotation");
MeshProp.AddPropertyString("");
MeshProp.AddPropertyString("A");
MeshProp.AddPropertyFloat64(0);
MeshProp.AddPropertyFloat64(0);
MeshProp.AddPropertyFloat64(0);
}
{
auto& MeshProp = MeshNodeProps.Children.Emplace("P");
MeshProp.AddPropertyString("DefaultAttributeIndex");
MeshProp.AddPropertyString("int");
MeshProp.AddPropertyString("Integer");
MeshProp.AddPropertyString("");
MeshProp.AddPropertyInteger32(0);
}
{
auto& MeshProp = MeshNodeProps.Children.Emplace("P");
MeshProp.AddPropertyString("InheritType");
MeshProp.AddPropertyString("enum");
MeshProp.AddPropertyString("");
MeshProp.AddPropertyString("");
MeshProp.AddPropertyInteger32(0);
}
MeshModelNode.Children.Emplace("MultiLayer").AddPropertyInteger32(0);
MeshModelNode.Children.Emplace("MultiTake").AddPropertyInteger32(0);
MeshModelNode.Children.Emplace("Shading").AddPropertyBoolean(true);
MeshModelNode.Children.Emplace("Culling").AddPropertyString("CullingOff");
auto& MeshNode = ObjectsNode.Children.Emplace("Geometry");
MeshNode.AddPropertyInteger64(UniqueId);
MeshNode.AddPropertyString(String::Format("KoreLibMesh%02d", SubmeshIndex) + String("\u0000\u0001Geometry", 10));
MeshNode.AddPropertyString("Mesh");
MeshNode.Children.EmplaceBack("Properties70");
MeshNode.Children.Emplace("GeometryVersion").AddPropertyInteger32(124);
auto& VertexBuffer = MeshNode.Children.Emplace("Vertices").Properties.Emplace('d', nullptr, 0);
for (auto& Vertex : Mesh.Vertices)
{
auto& Position = Vertex.Position();
VertexBuffer.AddValueFloat64(Position.X);
VertexBuffer.AddValueFloat64(Position.Y);
VertexBuffer.AddValueFloat64(Position.Z);
}
auto& FaceBuffer = MeshNode.Children.Emplace("PolygonVertexIndex").Properties.Emplace('i', nullptr, 0);
for (auto& Face : Mesh.Faces)
{
FaceBuffer.AddValueInteger32(Face[2]);
FaceBuffer.AddValueInteger32(Face[1]);
FaceBuffer.AddValueInteger32(0xffffffff ^ Face[0]);
}
auto& LayerNormals = MeshNode.Children.Emplace("LayerElementNormal");
LayerNormals.AddPropertyInteger32(0);
LayerNormals.Children.Emplace("Version").AddPropertyInteger32(101);
LayerNormals.Children.Emplace("Name").AddPropertyString("");
LayerNormals.Children.Emplace("MappingInformationType").AddPropertyString("ByVertice");
LayerNormals.Children.Emplace("ReferenceInformationType").AddPropertyString("Direct");
auto& NormalsBuffer = LayerNormals.Children.Emplace("Normals").Properties.Emplace('d', nullptr, 0);
for (auto& Vertex : Mesh.Vertices)
{
auto& Normal = Vertex.Normal();
NormalsBuffer.AddValueFloat64(Normal.X);
NormalsBuffer.AddValueFloat64(Normal.Y);
NormalsBuffer.AddValueFloat64(Normal.Z);
}
for (uint8_t i = 0; i < Mesh.Vertices.UVLayerCount(); i++)
{
auto& LayerUVs = MeshNode.Children.Emplace("LayerElementUV");
LayerUVs.AddPropertyInteger32(i);
LayerUVs.Children.Emplace("Version").AddPropertyInteger32(101);
LayerUVs.Children.Emplace("Name").AddPropertyString(String::Format("map%d", i + 1));
LayerUVs.Children.Emplace("MappingInformationType").AddPropertyString("ByVertice");
LayerUVs.Children.Emplace("ReferenceInformationType").AddPropertyString("Direct");
auto& UVsBuffer = LayerUVs.Children.Emplace("UV").Properties.Emplace('d', nullptr, 0);
for (auto& Vertex : Mesh.Vertices)
{
auto& UVLayer = Vertex.UVLayers(i);
UVsBuffer.AddValueFloat64(UVLayer.U);
UVsBuffer.AddValueFloat64((1.0l - UVLayer.V));
}
}
{
auto& LayerMats = MeshNode.Children.Emplace("LayerElementMaterial");
LayerMats.AddPropertyInteger32(0);
LayerMats.Children.Emplace("Version").AddPropertyInteger32(101);
LayerMats.Children.Emplace("Name").AddPropertyString("");
LayerMats.Children.Emplace("MappingInformationType").AddPropertyString("AllSame");
LayerMats.Children.Emplace("ReferenceInformationType").AddPropertyString("IndexToDirect");
auto& IndicesBuffer = LayerMats.Children.Emplace("Materials").Properties.Emplace('i', nullptr, 0);
IndicesBuffer.AddValueInteger32(0);
}
auto& LayerInfo = MeshNode.Children.Emplace("Layer");
LayerInfo.AddPropertyInteger32(0);
LayerInfo.Children.Emplace("Version").AddPropertyInteger32(100);
{
auto& LayerElement = LayerInfo.Children.Emplace("LayerElement");
LayerElement.Children.Emplace("Type").AddPropertyString("LayerElementNormal");
LayerElement.Children.Emplace("TypedIndex").AddPropertyInteger32(0);
}
{
auto& LayerElement = LayerInfo.Children.Emplace("LayerElement");
LayerElement.Children.Emplace("Type").AddPropertyString("LayerElementMaterial");
LayerElement.Children.Emplace("TypedIndex").AddPropertyInteger32(0);
}
{
auto& LayerElement = LayerInfo.Children.Emplace("LayerElement");
LayerElement.Children.Emplace("Type").AddPropertyString("LayerElementUV");
LayerElement.Children.Emplace("TypedIndex").AddPropertyInteger32(0);
}
for (uint8_t i = 1; i < Mesh.Vertices.UVLayerCount(); i++)
{
auto& LayerInfo = MeshNode.Children.Emplace("Layer");
LayerInfo.AddPropertyInteger32(i);
auto& LayerElement = LayerInfo.Children.Emplace("LayerElement");
LayerElement.Children.Emplace("Type").AddPropertyString("LayerElementUV");
LayerElement.Children.Emplace("TypedIndex").AddPropertyInteger32(i);
}
AddObjectConnection(ConnectionsNode, ModelId, RootModelId);
AddObjectConnection(ConnectionsNode, UniqueId, ModelId);
if (Mesh.MaterialIndices[0] >= 0)
AddObjectConnection(ConnectionsNode, MatIdMap[Mesh.MaterialIndices[0]], ModelId);
InitializeMeshSkinCluster(ObjectsNode, ConnectionsNode, SubmeshIndex, UniqueId, DeformerId);
uint64_t RootDeformer = DeformerId++;
Dictionary<int32_t, uint64_t> SubDeformers;
Dictionary<int32_t, KaydaraFBXNode*> SubDeformerNodes;
for (auto& Vertex : Mesh.Vertices)
{
for (uint8_t i = 0; i < Vertex.WeightCount(); i++)
{
auto& Weight = Vertex.Weights(i);
int32_t BoneId = Weight.Bone;
if (!SubDeformers.ContainsKey(BoneId))
{
// Make new subdeformer, connect to root, and add it
InitializeMeshSubDeformer(ObjectsNode, Model.Bones[BoneId], SubmeshIndex, BoneId, DeformerId);
// Add a connection
AddObjectConnection(ConnectionsNode, DeformerId, RootDeformer);
AddObjectConnection(ConnectionsNode, BoneIdMap[BoneId], DeformerId);
// Add it
SubDeformers.Add(BoneId, DeformerId++);
}
}
}
for (auto& SubDef : SubDeformers)
SubDeformerNodes.Add(SubDef.Key(), ObjectsNode.FindByUID(SubDef.Value()));
uint32_t VertexIndex = 0;
for (auto& Vertex : Mesh.Vertices)
{
for (uint8_t i = 0; i < Vertex.WeightCount(); i++)
{
auto& Weight = Vertex.Weights(i);
int32_t BoneId = Weight.Bone;
auto& DeformerNode = SubDeformerNodes[BoneId];
DeformerNode->Children[1].Properties[0].AddValueInteger32(VertexIndex);
DeformerNode->Children[2].Properties[0].AddValueFloat64(Weight.Value);
}
VertexIndex++;
}
UniqueId++;
SubmeshIndex++;
ModelId++;
}
AddObjectConnection(ConnectionsNode, RootModelId, 0);
AddObjectConnection(ConnectionsNode, RootJointsId, 0);
Document.Serialize(Writer);
return true;
}
imstring KaydaraFBX::ModelExtension()
{
return ".fbx";
}
imstring KaydaraFBX::AnimationExtension()
{
return ".fbx";
}
ExporterScale KaydaraFBX::ExportScale()
{
return ExporterScale::Default;
}
bool KaydaraFBX::SupportsAnimations()
{
return false;
}
bool KaydaraFBX::SupportsModels()
{
return true;
}
}