#include "stdafx.h" #include "KaydaraFBXContainer.h" namespace Assets::Exporters { constexpr std::tuple HeaderExtensionProperties[] = { {"FBXHeaderVersion", 1003}, {"FBXVersion", 7400}, {"EncryptionType", 0} }; constexpr std::tuple HeaderExtensionTimeProperties[] = { {"Version", 1000}, {"Year", 2019}, {"Month", 4}, {"Day", 26}, {"Hour", 10}, {"Minute", 39}, {"Second", 36}, {"Millisecond", 240} }; constexpr std::pair HeaderExtensionCreatorProperty = { "Creator", "KoreLib by DTZxPorter" }; constexpr std::pair HeaderFileIdNode = { "FileId", "\x28\xb3\x2a\xeb\xb6\x24\xcc\xc2\xbf\xc8\xb0\x2a\xa9\x2b\xfc\xf1" }; constexpr std::pair HeaderCreationTimeNode = { "CreationTime", "1970-01-01 10:00:00:000" }; constexpr std::tuple HeaderDefinitionsNode[] = { {"ObjectType", "GlobalSettings", ""}, {"ObjectType", "NodeAttribute", ""}, {"ObjectType", "Geometry", "FbxMesh"}, {"ObjectType", "Model", "FbxNode"}, {"ObjectType", "Pose", ""}, {"ObjectType", "Deformer", ""}, {"ObjectType", "Material", "FbxSurfacePhong"}, {"ObjectType", "Texture", "FbxFileTexture"} }; constexpr uint8_t FooterData[] = { 0xFA, 0xBC, 0xAB, 0x09, 0xD0, 0xC8, 0xD4, 0x66, 0xB1, 0x76, 0xFB, 0x83, 0x1C, 0xF7, 0x26, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x5A, 0x8C, 0x6A, 0xDE, 0xF5, 0xD9, 0x7E, 0xEC, 0xE9, 0x0C, 0xE3, 0x75, 0x8F, 0x29, 0x0B }; #pragma pack(push, 1) struct KaydaraFBXHeader { char Magic[0x15]; uint16_t VersionMinor; uint32_t VersionMajor; }; #pragma pack(pop) KaydaraFBXDocument::KaydaraFBXDocument() { this->InitializeFBXHeaderExtension(); this->InitializeGenerics(); this->InitializeGlobalSettings(); this->InitializeDocuments(); this->InitializeReferences(); this->InitializeDefinitions(); this->InitializeDynamics(); } void KaydaraFBXDocument::Serialize(IO::BinaryWriter& Writer) { Writer.Write({ "Kaydara FBX Binary ", 26, 7400 }); for (auto& Child : this->Nodes) Child.Prepare(); for (auto& Child : this->Nodes) Child.Serialize(Writer); uint8_t EmptyNode[13]{}; Writer.Write(EmptyNode, 0, sizeof(EmptyNode)); Writer.Write((uint8_t*)&FooterData[0], 0, sizeof(FooterData)); } KaydaraFBXNode& KaydaraFBXDocument::GetRootNode(const char* Name) { for (auto& Node : this->Nodes) if (Node.Name == Name) return Node; throw std::exception("No FBX node was found"); } void KaydaraFBXDocument::InitializeFBXHeaderExtension() { auto& Node = this->Nodes.Emplace("FBXHeaderExtension"); for (auto& Property : HeaderExtensionProperties) Node.Children.Emplace(std::get<0>(Property)).Properties.EmplaceBack('I', &std::get<1>(Property)); auto& CreationTime = Node.Children.Emplace("CreationTimeStamp"); for (auto& Property : HeaderExtensionTimeProperties) CreationTime.Children.Emplace(std::get<0>(Property)).Properties.EmplaceBack('I', &std::get<1>(Property)); } void KaydaraFBXDocument::InitializeGenerics() { String s1(HeaderFileIdNode.second); String s2(HeaderCreationTimeNode.second); String s3(HeaderExtensionCreatorProperty.second); this->Nodes.Emplace(HeaderFileIdNode.first).Properties.EmplaceBack('R', &s1); this->Nodes.Emplace(HeaderCreationTimeNode.first).Properties.EmplaceBack('S', &s2); this->Nodes.Emplace(HeaderExtensionCreatorProperty.first).Properties.EmplaceBack('S', &s3); } void KaydaraFBXDocument::InitializeGlobalSettings() { auto& Node = this->Nodes.Emplace("GlobalSettings"); Node.Children.Emplace("Version").AddPropertyInteger32(1000); auto& Properties = Node.Children.Emplace("Properties70"); { auto& Prop = Properties.Children.Emplace("P"); Prop.AddPropertyString("UpAxis"); Prop.AddPropertyString("int"); Prop.AddPropertyString("Integer"); Prop.AddPropertyString(""); Prop.AddPropertyInteger32(1); } { auto& Prop = Properties.Children.Emplace("P"); Prop.AddPropertyString("FrontAxis"); Prop.AddPropertyString("int"); Prop.AddPropertyString("Integer"); Prop.AddPropertyString(""); Prop.AddPropertyInteger32(2); } } void KaydaraFBXDocument::InitializeDocuments() { auto& Node = this->Nodes.Emplace("Documents"); Node.Children.Emplace("Count").AddPropertyInteger32(1); auto& Document = Node.Children.Emplace("Document"); Document.AddPropertyInteger64(318825901); Document.AddPropertyString("Scene"); Document.AddPropertyString("Scene"); Document.Children.Emplace("RootNode").AddPropertyInteger64(0); } void KaydaraFBXDocument::InitializeReferences() { this->Nodes.EmplaceBack("References"); } void KaydaraFBXDocument::InitializeDefinitions() { auto& Node = this->Nodes.Emplace("Definitions"); Node.Children.Emplace("Version").AddPropertyInteger32(100); Node.Children.Emplace("Count").AddPropertyInteger32(ARRAYSIZE(HeaderDefinitionsNode)); for (auto& Type : HeaderDefinitionsNode) { auto& Definition = Node.Children.Emplace(std::get<0>(Type)); Definition.AddPropertyString(std::get<1>(Type)); Definition.Children.Emplace("Count").AddPropertyInteger32(1); if (strlen(std::get<2>(Type))) Definition.Children.Emplace("PropertyTemplate").AddPropertyString(std::get<2>(Type)); } } void KaydaraFBXDocument::InitializeDynamics() { // Create each dynamic node auto& NodeObjects = this->Nodes.Emplace("Objects"); auto& NodeConnections = this->Nodes.Emplace("Connections"); auto& NodeTakes = this->Nodes.Emplace("Takes"); NodeTakes.Children.Emplace("Current").AddPropertyString(""); } KaydaraFBXProperty::KaydaraFBXProperty() : _Type(0), _IntegralValue{} { } KaydaraFBXProperty::KaydaraFBXProperty(const char Type, const void* Data) : _Type(Type), _IntegralValue{} { switch (Type) { case 'Y': _IntegralValue.Integer16 = *(uint16_t*)Data; break; case 'C': _IntegralValue.Boolean = *(bool*)Data; break; case 'I': _IntegralValue.Integer32 = *(uint32_t*)Data; break; case 'F': _IntegralValue.Float32 = *(float*)Data; break; case 'D': _IntegralValue.Float64 = *(double*)Data; break; case 'L': _IntegralValue.Integer64 = *(uint64_t*)Data; break; case 'R': case 'S': _StringValue = *(String*)Data; break; } } KaydaraFBXProperty::KaydaraFBXProperty(const char Type, const void* Data, const uint32_t Count) : _Type(Type), _IntegralValue{} { if (Data == nullptr || Count == 0) return; for (uint32_t i = 0; i < Count; i++) { KaydaraFBXIntegralProperty Property; switch (Type) { case 'b': Property.Boolean = ((bool*)Data)[i]; break; case 'i': Property.Integer32 = ((uint32_t*)Data)[i]; break; case 'f': Property.Float32 = ((float*)Data)[i]; break; case 'd': Property.Float64 = ((double*)Data)[i]; break; case 'l': Property.Integer64 = ((uint64_t*)Data)[i]; break; } this->_IntegralArrayValues.EmplaceBack(std::move(Property)); } } void KaydaraFBXProperty::AddValueBoolean(bool Value) { KaydaraFBXIntegralProperty Property; Property.Boolean = Value; this->_IntegralArrayValues.EmplaceBack(std::move(Property)); } void KaydaraFBXProperty::AddValueInteger32(uint32_t Value) { KaydaraFBXIntegralProperty Property; Property.Integer32 = Value; this->_IntegralArrayValues.EmplaceBack(std::move(Property)); } void KaydaraFBXProperty::AddValueInteger64(uint64_t Value) { KaydaraFBXIntegralProperty Property; Property.Integer64 = Value; this->_IntegralArrayValues.EmplaceBack(std::move(Property)); } void KaydaraFBXProperty::AddValueFloat32(float Value) { KaydaraFBXIntegralProperty Property; Property.Float32 = Value; this->_IntegralArrayValues.EmplaceBack(std::move(Property)); } void KaydaraFBXProperty::AddValueFloat64(double Value) { KaydaraFBXIntegralProperty Property; Property.Float64 = Value; this->_IntegralArrayValues.EmplaceBack(std::move(Property)); } void KaydaraFBXProperty::Serialize(IO::BinaryWriter& Writer) { Writer.Write(this->_Type); switch (this->_Type) { case 'Y': Writer.Write(this->_IntegralValue.Integer16); break; case 'C': Writer.Write(this->_IntegralValue.Boolean); break; case 'I': Writer.Write(this->_IntegralValue.Integer32); break; case 'F': Writer.Write(this->_IntegralValue.Float32); break; case 'D': Writer.Write(this->_IntegralValue.Float64); break; case 'L': Writer.Write(this->_IntegralValue.Integer64); break; case 'R': case 'S': Writer.Write(this->_StringValue.Length()); Writer.Write((uint8_t*)&this->_StringValue[0], 0, this->_StringValue.Length()); break; default: Writer.Write(this->_IntegralArrayValues.Count()); Writer.Write(0); { uint32_t CompressedLength = this->_IntegralArrayValues.Count(); switch (this->_Type) { case 'f': CompressedLength *= sizeof(float); break; case 'd': CompressedLength *= sizeof(double); break; case 'l': CompressedLength *= sizeof(uint64_t); break; case 'i': CompressedLength *= sizeof(uint32_t); break; case 'b': CompressedLength *= sizeof(bool); break; } Writer.Write(CompressedLength); for (auto& Value : this->_IntegralArrayValues) { switch (this->_Type) { case 'f': Writer.Write(Value.Float32); break; case 'd': Writer.Write(Value.Float64); break; case 'l': Writer.Write(Value.Integer64); break; case 'i': Writer.Write(Value.Integer32); break; case 'b': Writer.Write(Value.Boolean); break; } } } break; } } const uint32_t KaydaraFBXProperty::GetLength() const { switch (this->_Type) { case 'Y': return sizeof(uint16_t) + sizeof(char); case 'C': return sizeof(bool) + sizeof(char); case 'I': return sizeof(uint32_t) + sizeof(char); case 'F': return sizeof(float) + sizeof(char); case 'D': return sizeof(double) + sizeof(char); case 'L': return sizeof(uint64_t) + sizeof(char); case 'S': case 'R': return this->_StringValue.Length() + sizeof(uint32_t) + sizeof(char); case 'f': return this->_IntegralArrayValues.Count() * sizeof(float) + 13; case 'd': return this->_IntegralArrayValues.Count() * sizeof(double) + 13; case 'l': return this->_IntegralArrayValues.Count() * sizeof(uint64_t) + 13; case 'i': return this->_IntegralArrayValues.Count() * sizeof(uint32_t) + 13; case 'b': return this->_IntegralArrayValues.Count() * sizeof(bool) + 13; default: return 0; } } KaydaraFBXIntegralProperty KaydaraFBXProperty::GetIntegralValue() const { return this->_IntegralValue; } KaydaraFBXNode::KaydaraFBXNode(const String& Name) : Name(Name) { } KaydaraFBXNode::KaydaraFBXNode(const char* Name) : Name(Name) { } void KaydaraFBXNode::Prepare() { for (auto& Child : this->Children) Child.Prepare(); if (this->Children.Count() > 0 || (this->Properties.Count() == 0 && this->Name.Length() > 0)) this->Children.EmplaceBack(); } void KaydaraFBXNode::Serialize(IO::BinaryWriter& Writer) { if (this->Name.Length() == 0 && this->Children.Count() == 0 && this->Properties.Count() == 0) { uint8_t EmptyNode[13]{}; Writer.Write(EmptyNode, 0, sizeof(EmptyNode)); return; } uint32_t PropertyListLength = 0; uint32_t NodeLength = 13 + Name.Length(); for (auto& Property : this->Properties) PropertyListLength += Property.GetLength(); NodeLength += PropertyListLength; for (auto& Child : this->Children) NodeLength += Child.GetLength(); Writer.Write((uint32_t)Writer.GetBaseStream()->GetPosition() + NodeLength); Writer.Write(this->Properties.Count()); Writer.Write(PropertyListLength); Writer.Write((uint8_t)Name.Length()); Writer.Write((uint8_t*)&Name[0], 0, Name.Length()); for (auto& Property : this->Properties) Property.Serialize(Writer); for (auto& Child : this->Children) Child.Serialize(Writer); } void KaydaraFBXNode::AddPropertyBoolean(bool Value) { this->Properties.EmplaceBack('C', &Value); } void KaydaraFBXNode::AddPropertyInteger16(uint16_t Value) { this->Properties.EmplaceBack('Y', &Value); } void KaydaraFBXNode::AddPropertyInteger32(uint32_t Value) { this->Properties.EmplaceBack('I', &Value); } void KaydaraFBXNode::AddPropertyInteger64(uint64_t Value) { this->Properties.EmplaceBack('L', &Value); } void KaydaraFBXNode::AddPropertyFloat32(float Value) { this->Properties.EmplaceBack('F', &Value); } void KaydaraFBXNode::AddPropertyFloat64(double Value) { this->Properties.EmplaceBack('D', &Value); } void KaydaraFBXNode::AddPropertyString(const String& Value) { this->Properties.EmplaceBack('S', &Value); } void KaydaraFBXNode::AddPropertyString(const char* Value) { String s(Value); this->Properties.EmplaceBack('S', &s); } void KaydaraFBXNode::AddPropertyString(const char* Value, const uint32_t Length) { String s(Value, Length); this->Properties.EmplaceBack('S', &s); } void KaydaraFBXNode::AddPropertyRaw(const char* Value) { String s(Value); this->Properties.EmplaceBack('R', &s); } void KaydaraFBXNode::AddPropertyRaw(const char* Value, const uint32_t Length) { String s(Value, Length); this->Properties.EmplaceBack('R', &s); } KaydaraFBXNode* KaydaraFBXNode::FindByUID(const uint64_t UID) { for (auto& Child : this->Children) { if (Child.Properties.Count() > 0 && Child.Properties[0].GetIntegralValue().Integer64 == UID) return &Child; } return nullptr; } const uint32_t KaydaraFBXNode::GetLength() const { uint32_t Result = 13 + Name.Length(); for (auto& Child : this->Children) Result += Child.GetLength(); for (auto& Property : this->Properties) Result += Property.GetLength(); return Result; } }