#include "fbxdocument.h" #include "fbxutil.h" using std::string; using std::cout; using std::endl; using std::ifstream; using std::ofstream; using std::uint32_t; using std::uint8_t; namespace fbx { FBXDocument::FBXDocument() { version = 7400; } void FBXDocument::read(string fname) { ifstream file; // buffer constexpr int bufferSize = 1 << 16; char buffer[bufferSize]; file.rdbuf()->pubsetbuf(buffer, bufferSize); file.open(fname, std::ios::in | std::ios::binary); if (file.is_open()) { read(file); } else { throw std::string("Cannot read from file: \"" + fname + "\""); } file.close(); } void FBXDocument::write(string fname) { ofstream file; // buffer constexpr int bufferSize = 1 << 16; char buffer[bufferSize]; file.rdbuf()->pubsetbuf(buffer, bufferSize); file.open(fname, std::ios::out | std::ios::binary); if (file.is_open()) { write(file); } else { throw std::string("Cannot write to file: \"" + fname + "\""); } file.close(); } bool checkMagic(Reader &reader) { string magic("Kaydara FBX Binary "); for(char c : magic) { if(reader.readUint8() != c) return false; } if(reader.readUint8() != 0x00) return false; if(reader.readUint8() != 0x1A) return false; if(reader.readUint8() != 0x00) return false; return true; } void FBXDocument::read(std::ifstream &input) { Reader reader(&input); input >> std::noskipws; if(!checkMagic(reader)) throw std::string("Not a FBX file"); uint32_t version = reader.readUint32(); uint32_t maxVersion = 7400; if(version > maxVersion) throw "Unsupported FBX version "+std::to_string(version) + " latest supported version is "+std::to_string(maxVersion); uint32_t start_offset = 27; // magic: 21+2, version: 4 do{ FBXNode node; start_offset += node.read(input, start_offset); if(node.isNull()) break; nodes.push_back(node); } while(true); } namespace { void writerFooter(Writer &writer) { uint8_t footer[] = { 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, 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 }; for(unsigned int i = 0; i < sizeof(footer); i++) { writer.write(footer[i]); } } } void FBXDocument::write(std::ofstream &output) { Writer writer(&output); writer.write("Kaydara FBX Binary "); writer.write((uint8_t) 0); writer.write((uint8_t) 0x1A); writer.write((uint8_t) 0); writer.write(version); uint32_t offset = 27; // magic: 21+2, version: 4 for(FBXNode node : nodes) { offset += node.write(output, offset); } FBXNode nullNode; offset += nullNode.write(output, offset); writerFooter(writer); } void FBXDocument::createBasicStructure() { FBXNode headerExtension("FBXHeaderExtension"); headerExtension.addPropertyNode("FBXHeaderVersion", (int32_t) 1003); headerExtension.addPropertyNode("FBXVersion", (int32_t) getVersion()); headerExtension.addPropertyNode("EncryptionType", (int32_t) 0); { FBXNode creationTimeStamp("CreationTimeStamp"); creationTimeStamp.addPropertyNode("Version", (int32_t) 1000); creationTimeStamp.addPropertyNode("Year", (int32_t) 2017); creationTimeStamp.addPropertyNode("Month", (int32_t) 5); creationTimeStamp.addPropertyNode("Day", (int32_t) 2); creationTimeStamp.addPropertyNode("Hour", (int32_t) 14); creationTimeStamp.addPropertyNode("Minute", (int32_t) 11); creationTimeStamp.addPropertyNode("Second", (int32_t) 46); creationTimeStamp.addPropertyNode("Millisecond", (int32_t) 917); headerExtension.addChild(creationTimeStamp); } headerExtension.addPropertyNode("Creator", "Blender (stable FBX IO) - 2.78 (sub 0) - 3.7.7"); { FBXNode sceneInfo("SceneInfo"); sceneInfo.addProperty(std::vector({'G','l','o','b','a','l','I','n','f','o',0,1,'S','c','e','n','e','I','n','f','o'}), 'S'); sceneInfo.addProperty("UserData"); sceneInfo.addPropertyNode("Type", "UserData"); sceneInfo.addPropertyNode("Version", 100); { FBXNode metadata("MetaData"); metadata.addPropertyNode("Version", 100); metadata.addPropertyNode("Title", ""); metadata.addPropertyNode("Subject", ""); metadata.addPropertyNode("Author", ""); metadata.addPropertyNode("Keywords", ""); metadata.addPropertyNode("Revision", ""); metadata.addPropertyNode("Comment", ""); sceneInfo.addChild(metadata); } { FBXNode properties("Properties70"); { FBXNode p("P"); p.addProperty("DocumentUrl"); p.addProperty("KString"); p.addProperty("Url"); p.addProperty(""); p.addProperty("/foobar.fbx"); properties.addChild(p); } { FBXNode p("P"); p.addProperty("SrcDocumentUrl"); p.addProperty("KString"); p.addProperty("Url"); p.addProperty(""); p.addProperty("/foobar.fbx"); properties.addChild(p); } { FBXNode p("P"); p.addProperty("Original"); p.addProperty("Compound"); p.addProperty(""); p.addProperty(""); properties.addChild(p); } { FBXNode p("P"); p.addProperty("Original|ApplicationVendor"); p.addProperty("KString"); p.addProperty(""); p.addProperty(""); p.addProperty("Blender Foundation"); properties.addChild(p); } { FBXNode p("P"); p.addProperty("Original|ApplicationName"); p.addProperty("KString"); p.addProperty(""); p.addProperty(""); p.addProperty("Blender (stable FBX IO)"); properties.addChild(p); } { FBXNode p("P"); p.addProperty("Original|ApplicationVersion"); p.addProperty("KString"); p.addProperty(""); p.addProperty(""); p.addProperty("2.78 (sub 0)"); properties.addChild(p); } { FBXNode p("P"); p.addProperty("Original|DateTime_GMT"); p.addProperty("DateTime"); p.addProperty(""); p.addProperty(""); p.addProperty("01/01/1970 00:00:00.000"); properties.addChild(p); } { FBXNode p("P"); p.addProperty("Original|FileName"); p.addProperty("KString"); p.addProperty(""); p.addProperty(""); p.addProperty("/foobar.fbx"); properties.addChild(p); } { FBXNode p("P"); p.addProperty("LastSaved"); p.addProperty("Compound"); p.addProperty(""); p.addProperty(""); properties.addChild(p); } { FBXNode p("P"); p.addProperty("LastSaved|ApplicationVendor"); p.addProperty("KString"); p.addProperty(""); p.addProperty(""); p.addProperty("Blender Foundation"); properties.addChild(p); } { FBXNode p("P"); p.addProperty("LastSaved|ApplicationName"); p.addProperty("KString"); p.addProperty(""); p.addProperty(""); p.addProperty("Blender (stable FBX IO)"); properties.addChild(p); } { FBXNode p("P"); p.addProperty("LastSaved|DateTime_GMT"); p.addProperty("DateTime"); p.addProperty(""); p.addProperty(""); p.addProperty("01/01/1970 00:00:00.000"); properties.addChild(p); } sceneInfo.addChild(properties); } headerExtension.addChild(sceneInfo); } nodes.push_back(headerExtension); } std::uint32_t FBXDocument::getVersion() { return version; } void FBXDocument::print() { cout << "{\n"; cout << " \"version\": " << getVersion() << ",\n"; cout << " \"children\": [\n"; bool hasPrev = false; for(auto node : nodes) { if(hasPrev) cout << ",\n"; node.print(" "); hasPrev = true; } cout << "\n ]\n}" << endl; } } // namespace fbx