/// <summary> /// Writes an FBX document to the stream /// </summary> /// <param name="document"></param> /// <remarks> /// ASCII FBX files have no header or footer, so you can call this multiple times /// </remarks> public void Write(FbxDocument document) { if (document == null) { throw new ArgumentNullException(nameof(document)); } var sb = new StringBuilder(); // Write version header (a comment, but required for many importers) var vMajor = (int)document.Version / 1000; var vMinor = ((int)document.Version % 1000) / 100; var vRev = ((int)document.Version % 100) / 10; sb.Append($"; FBX {vMajor}.{vMinor}.{vRev} project file\n\n"); nodePath.Clear(); foreach (var n in document.Nodes) { if (n == null) { continue; } BuildString(n, sb, document.Version >= FbxVersion.v7_1); sb.Append('\n'); } var b = Encoding.ASCII.GetBytes(sb.ToString()); stream.Write(b, 0, b.Length); }
/// <summary> /// Reads an FBX document from the stream /// </summary> /// <returns>The top-level node</returns> /// <exception cref="FbxException">The FBX data was malformed /// for the reader's error level</exception> public FbxDocument Read() { // Read header bool validHeader = ReadHeader(stream.BaseStream); if (errorLevel >= ErrorLevel.Strict && !validHeader) { throw new FbxException(stream.BaseStream.Position, "Invalid header string"); } var document = new FbxDocument { Version = (FbxVersion)stream.ReadInt32() }; // Read nodes var dataPos = stream.BaseStream.Position; FbxNode nested; do { nested = ReadNode((int)document.Version); if (nested != null) { document.Nodes.Add(nested); } } while (nested != null); // Read footer code var footerCode = new byte[footerCodeSize]; stream.BaseStream.Read(footerCode, 0, footerCode.Length); if (errorLevel >= ErrorLevel.Strict) { var validCode = GenerateFooterCode(document); if (!CheckEqual(footerCode, validCode)) { throw new FbxException(stream.BaseStream.Position - footerCodeSize, "Incorrect footer code"); } } // Read footer extension dataPos = stream.BaseStream.Position; var validFooterExtension = CheckFooter(stream, document.Version); if (errorLevel >= ErrorLevel.Strict && !validFooterExtension) { throw new FbxException(dataPos, "Invalid footer"); } return(document); }
/// <summary> /// Writes an FBX file to the output /// </summary> /// <param name="document"></param> public void Write(FbxDocument document) { stream.BaseStream.Position = 0; WriteHeader(stream.BaseStream); stream.Write((int)document.Version); // TODO: Do we write a top level node or not? Maybe check the version? nodePath.Clear(); foreach (var node in document.Nodes) { WriteNode(node); } WriteNode(null); stream.Write(GenerateFooterCode(document)); WriteFooter(stream, (int)document.Version); output.Write(memory.GetBuffer(), 0, (int)memory.Position); }
/// <summary> /// Reads a full document from the stream /// </summary> /// <returns>The complete document object</returns> public FbxDocument Read() { var ret = new FbxDocument(); // Read version string const string versionString = @"; FBX (\d)\.(\d)\.(\d) project file"; char c; while (char.IsWhiteSpace(c = ReadChar()) && !endStream) { } // Skip whitespace bool hasVersionString = false; if (c == ';') { var sb = new StringBuilder(); do { sb.Append(c); } while (!IsLineEnd(c = ReadChar()) && !endStream); var match = Regex.Match(sb.ToString(), versionString); hasVersionString = match.Success; if (hasVersionString) { ret.Version = (FbxVersion)( int.Parse(match.Groups[1].Value) * 1000 + int.Parse(match.Groups[2].Value) * 100 + int.Parse(match.Groups[3].Value) * 10 ); } } if (!hasVersionString && errorLevel >= ErrorLevel.Strict) { throw new FbxException(line, column, "Invalid version string; first line must match \"" + versionString + "\""); } FbxNode node; while ((node = ReadNode()) != null) { ret.Nodes.Add(node); } return(ret); }