parent
e549705af0
commit
826243f289
|
@ -98,7 +98,7 @@ void ExportSceneAssbin(const char*, IOSystem*, const aiScene*, const ExportPrope
|
|||
void ExportSceneAssxml(const char*, IOSystem*, const aiScene*, const ExportProperties*);
|
||||
void ExportSceneX3D(const char*, IOSystem*, const aiScene*, const ExportProperties*);
|
||||
void ExportSceneFBX(const char*, IOSystem*, const aiScene*, const ExportProperties*);
|
||||
//void ExportSceneFBXA(const char*, IOSystem*, const aiScene*, const ExportProperties*);
|
||||
void ExportSceneFBXA(const char*, IOSystem*, const aiScene*, const ExportProperties*);
|
||||
void ExportScene3MF( const char*, IOSystem*, const aiScene*, const ExportProperties* );
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
@ -173,7 +173,7 @@ Exporter::ExportFormatEntry gExporters[] =
|
|||
|
||||
#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
|
||||
Exporter::ExportFormatEntry( "fbx", "Autodesk FBX (binary)", "fbx", &ExportSceneFBX, 0 ),
|
||||
//Exporter::ExportFormatEntry( "fbxa", "Autodesk FBX (ascii)", "fbx", &ExportSceneFBXA, 0 ),
|
||||
Exporter::ExportFormatEntry( "fbxa", "Autodesk FBX (ascii)", "fbx", &ExportSceneFBXA, 0 ),
|
||||
#endif
|
||||
|
||||
#ifndef ASSIMP_BUILD_NO_3MF_EXPORTER
|
||||
|
|
|
@ -45,10 +45,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "FBXCommon.h"
|
||||
|
||||
#include <assimp/StreamWriter.h> // StreamWriterLE
|
||||
#include <assimp/Exceptional.h> // DeadlyExportError
|
||||
#include <assimp/ai_assert.h>
|
||||
|
||||
#include <string>
|
||||
#include <ostream>
|
||||
#include <sstream> // ostringstream
|
||||
#include <memory> // shared_ptr
|
||||
#include <cstdio> // snprintf
|
||||
|
||||
// AddP70<type> helpers... there's no usable pattern here,
|
||||
// so all are defined as separate functions.
|
||||
|
@ -145,33 +149,174 @@ void FBX::Node::AddP70time(
|
|||
}
|
||||
|
||||
|
||||
// public member functions for writing nodes to stream
|
||||
|
||||
void FBX::Node::Dump(
|
||||
std::shared_ptr<Assimp::IOStream> outfile,
|
||||
bool binary, int indent
|
||||
) {
|
||||
if (binary) {
|
||||
Assimp::StreamWriterLE outstream(outfile);
|
||||
DumpBinary(outstream);
|
||||
} else {
|
||||
std::ostringstream ss;
|
||||
DumpAscii(ss, indent);
|
||||
std::string s = ss.str();
|
||||
outfile->Write(s.c_str(), s.size(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
void FBX::Node::Dump(
|
||||
Assimp::StreamWriterLE &outstream,
|
||||
bool binary, int indent
|
||||
) {
|
||||
if (binary) {
|
||||
DumpBinary(outstream);
|
||||
} else {
|
||||
std::ostringstream ss;
|
||||
DumpAscii(ss, indent);
|
||||
outstream.PutString(ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// public member functions for low-level writing
|
||||
|
||||
void FBX::Node::Begin(
|
||||
Assimp::StreamWriterLE &s,
|
||||
bool binary, int indent
|
||||
) {
|
||||
if (binary) {
|
||||
BeginBinary(s);
|
||||
} else {
|
||||
// assume we're at the correct place to start already
|
||||
(void)indent;
|
||||
std::ostringstream ss;
|
||||
BeginAscii(ss, indent);
|
||||
s.PutString(ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
void FBX::Node::DumpProperties(
|
||||
Assimp::StreamWriterLE& s,
|
||||
bool binary, int indent
|
||||
) {
|
||||
if (binary) {
|
||||
DumpPropertiesBinary(s);
|
||||
} else {
|
||||
std::ostringstream ss;
|
||||
DumpPropertiesAscii(ss, indent);
|
||||
s.PutString(ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
void FBX::Node::EndProperties(
|
||||
Assimp::StreamWriterLE &s,
|
||||
bool binary, int indent
|
||||
) {
|
||||
EndProperties(s, binary, indent, properties.size());
|
||||
}
|
||||
|
||||
void FBX::Node::EndProperties(
|
||||
Assimp::StreamWriterLE &s,
|
||||
bool binary, int indent,
|
||||
size_t num_properties
|
||||
) {
|
||||
if (binary) {
|
||||
EndPropertiesBinary(s, num_properties);
|
||||
} else {
|
||||
// nothing to do
|
||||
(void)indent;
|
||||
}
|
||||
}
|
||||
|
||||
void FBX::Node::BeginChildren(
|
||||
Assimp::StreamWriterLE &s,
|
||||
bool binary, int indent
|
||||
) {
|
||||
if (binary) {
|
||||
// nothing to do
|
||||
} else {
|
||||
std::ostringstream ss;
|
||||
BeginChildrenAscii(ss, indent);
|
||||
s.PutString(ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
void FBX::Node::DumpChildren(
|
||||
Assimp::StreamWriterLE& s,
|
||||
bool binary, int indent
|
||||
) {
|
||||
if (binary) {
|
||||
DumpChildrenBinary(s);
|
||||
} else {
|
||||
std::ostringstream ss;
|
||||
DumpChildrenAscii(ss, indent);
|
||||
s.PutString(ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
void FBX::Node::End(
|
||||
Assimp::StreamWriterLE &s,
|
||||
bool binary, int indent,
|
||||
bool has_children
|
||||
) {
|
||||
if (binary) {
|
||||
EndBinary(s, has_children);
|
||||
} else {
|
||||
std::ostringstream ss;
|
||||
EndAscii(ss, indent, has_children);
|
||||
s.PutString(ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// public member functions for writing to binary fbx
|
||||
|
||||
void FBX::Node::Dump(std::shared_ptr<Assimp::IOStream> outfile)
|
||||
{
|
||||
Assimp::StreamWriterLE outstream(outfile);
|
||||
Dump(outstream);
|
||||
}
|
||||
|
||||
void FBX::Node::Dump(Assimp::StreamWriterLE &s)
|
||||
void FBX::Node::DumpBinary(Assimp::StreamWriterLE &s)
|
||||
{
|
||||
// write header section (with placeholders for some things)
|
||||
Begin(s);
|
||||
BeginBinary(s);
|
||||
|
||||
// write properties
|
||||
DumpProperties(s);
|
||||
DumpPropertiesBinary(s);
|
||||
|
||||
// go back and fill in property related placeholders
|
||||
EndProperties(s, properties.size());
|
||||
EndPropertiesBinary(s, properties.size());
|
||||
|
||||
// write children
|
||||
DumpChildren(s);
|
||||
DumpChildrenBinary(s);
|
||||
|
||||
// finish, filling in end offset placeholder
|
||||
End(s, force_has_children || !children.empty());
|
||||
EndBinary(s, force_has_children || !children.empty());
|
||||
}
|
||||
|
||||
void FBX::Node::Begin(Assimp::StreamWriterLE &s)
|
||||
|
||||
// public member functions for writing to ascii fbx
|
||||
|
||||
void FBX::Node::DumpAscii(std::ostream &s, int indent)
|
||||
{
|
||||
// write name
|
||||
BeginAscii(s, indent);
|
||||
|
||||
// write properties
|
||||
DumpPropertiesAscii(s, indent);
|
||||
|
||||
if (force_has_children || !children.empty()) {
|
||||
// begin children (with a '{')
|
||||
BeginChildrenAscii(s, indent + 1);
|
||||
// write children
|
||||
DumpChildrenAscii(s, indent + 1);
|
||||
}
|
||||
|
||||
// finish (also closing the children bracket '}')
|
||||
EndAscii(s, indent, force_has_children || !children.empty());
|
||||
}
|
||||
|
||||
|
||||
// private member functions for low-level writing to fbx
|
||||
|
||||
void FBX::Node::BeginBinary(Assimp::StreamWriterLE &s)
|
||||
{
|
||||
// remember start pos so we can come back and write the end pos
|
||||
this->start_pos = s.Tell();
|
||||
|
@ -189,26 +334,14 @@ void FBX::Node::Begin(Assimp::StreamWriterLE &s)
|
|||
this->property_start = s.Tell();
|
||||
}
|
||||
|
||||
void FBX::Node::DumpProperties(Assimp::StreamWriterLE& s)
|
||||
void FBX::Node::DumpPropertiesBinary(Assimp::StreamWriterLE& s)
|
||||
{
|
||||
for (auto &p : properties) {
|
||||
p.Dump(s);
|
||||
p.DumpBinary(s);
|
||||
}
|
||||
}
|
||||
|
||||
void FBX::Node::DumpChildren(Assimp::StreamWriterLE& s)
|
||||
{
|
||||
for (FBX::Node& child : children) {
|
||||
child.Dump(s);
|
||||
}
|
||||
}
|
||||
|
||||
void FBX::Node::EndProperties(Assimp::StreamWriterLE &s)
|
||||
{
|
||||
EndProperties(s, properties.size());
|
||||
}
|
||||
|
||||
void FBX::Node::EndProperties(
|
||||
void FBX::Node::EndPropertiesBinary(
|
||||
Assimp::StreamWriterLE &s,
|
||||
size_t num_properties
|
||||
) {
|
||||
|
@ -222,7 +355,14 @@ void FBX::Node::EndProperties(
|
|||
s.Seek(pos);
|
||||
}
|
||||
|
||||
void FBX::Node::End(
|
||||
void FBX::Node::DumpChildrenBinary(Assimp::StreamWriterLE& s)
|
||||
{
|
||||
for (FBX::Node& child : children) {
|
||||
child.DumpBinary(s);
|
||||
}
|
||||
}
|
||||
|
||||
void FBX::Node::EndBinary(
|
||||
Assimp::StreamWriterLE &s,
|
||||
bool has_children
|
||||
) {
|
||||
|
@ -237,48 +377,192 @@ void FBX::Node::End(
|
|||
}
|
||||
|
||||
|
||||
// static member functions
|
||||
void FBX::Node::BeginAscii(std::ostream& s, int indent)
|
||||
{
|
||||
s << '\n';
|
||||
for (int i = 0; i < indent; ++i) { s << '\t'; }
|
||||
s << name << ": ";
|
||||
}
|
||||
|
||||
// convenience function to create and write a property node,
|
||||
// holding a single property which is an array of values.
|
||||
// does not copy the data, so is efficient for large arrays.
|
||||
void FBX::Node::DumpPropertiesAscii(std::ostream &s, int indent)
|
||||
{
|
||||
for (size_t i = 0; i < properties.size(); ++i) {
|
||||
if (i > 0) { s << ", "; }
|
||||
properties[i].DumpAscii(s, indent);
|
||||
}
|
||||
}
|
||||
|
||||
void FBX::Node::BeginChildrenAscii(std::ostream& s, int indent)
|
||||
{
|
||||
// only call this if there are actually children
|
||||
s << " {";
|
||||
(void)indent;
|
||||
}
|
||||
|
||||
void FBX::Node::DumpChildrenAscii(std::ostream& s, int indent)
|
||||
{
|
||||
// children will need a lot of padding and corralling
|
||||
if (children.size() || force_has_children) {
|
||||
for (size_t i = 0; i < children.size(); ++i) {
|
||||
// no compression in ascii files, so skip this node if it exists
|
||||
if (children[i].name == "EncryptionType") { continue; }
|
||||
// the child can dump itself
|
||||
children[i].DumpAscii(s, indent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FBX::Node::EndAscii(std::ostream& s, int indent, bool has_children)
|
||||
{
|
||||
if (!has_children) { return; } // nothing to do
|
||||
s << '\n';
|
||||
for (int i = 0; i < indent; ++i) { s << '\t'; }
|
||||
s << "}";
|
||||
}
|
||||
|
||||
// private helpers for static member functions
|
||||
|
||||
// ascii property node from vector of doubles
|
||||
void FBX::Node::WritePropertyNodeAscii(
|
||||
const std::string& name,
|
||||
const std::vector<double>& v,
|
||||
Assimp::StreamWriterLE& s,
|
||||
int indent
|
||||
){
|
||||
char buffer[32];
|
||||
FBX::Node node(name);
|
||||
node.Begin(s, false, indent);
|
||||
std::string vsize = std::to_string(v.size());
|
||||
// *<size> {
|
||||
s.PutChar('*'); s.PutString(vsize); s.PutString(" {\n");
|
||||
// indent + 1
|
||||
for (int i = 0; i < indent + 1; ++i) { s.PutChar('\t'); }
|
||||
// a: value,value,value,...
|
||||
s.PutString("a: ");
|
||||
int count = 0;
|
||||
for (size_t i = 0; i < v.size(); ++i) {
|
||||
if (i > 0) { s.PutChar(','); }
|
||||
int len = snprintf(buffer, sizeof(buffer), "%f", v[i]);
|
||||
count += len;
|
||||
if (count > 2048) { s.PutChar('\n'); count = 0; }
|
||||
if (len < 0 || len > 31) {
|
||||
// this should never happen
|
||||
throw DeadlyExportError("failed to convert double to string");
|
||||
}
|
||||
for (int j = 0; j < len; ++j) { s.PutChar(buffer[j]); }
|
||||
}
|
||||
// }
|
||||
s.PutChar('\n');
|
||||
for (int i = 0; i < indent; ++i) { s.PutChar('\t'); }
|
||||
s.PutChar('}'); s.PutChar(' ');
|
||||
node.End(s, false, indent, false);
|
||||
}
|
||||
|
||||
// ascii property node from vector of int32_t
|
||||
void FBX::Node::WritePropertyNodeAscii(
|
||||
const std::string& name,
|
||||
const std::vector<int32_t>& v,
|
||||
Assimp::StreamWriterLE& s,
|
||||
int indent
|
||||
){
|
||||
char buffer[32];
|
||||
FBX::Node node(name);
|
||||
node.Begin(s, false, indent);
|
||||
std::string vsize = std::to_string(v.size());
|
||||
// *<size> {
|
||||
s.PutChar('*'); s.PutString(vsize); s.PutString(" {\n");
|
||||
// indent + 1
|
||||
for (int i = 0; i < indent + 1; ++i) { s.PutChar('\t'); }
|
||||
// a: value,value,value,...
|
||||
s.PutString("a: ");
|
||||
int count = 0;
|
||||
for (size_t i = 0; i < v.size(); ++i) {
|
||||
if (i > 0) { s.PutChar(','); }
|
||||
int len = snprintf(buffer, sizeof(buffer), "%d", v[i]);
|
||||
count += len;
|
||||
if (count > 2048) { s.PutChar('\n'); count = 0; }
|
||||
if (len < 0 || len > 31) {
|
||||
// this should never happen
|
||||
throw DeadlyExportError("failed to convert double to string");
|
||||
}
|
||||
for (int j = 0; j < len; ++j) { s.PutChar(buffer[j]); }
|
||||
}
|
||||
// }
|
||||
s.PutChar('\n');
|
||||
for (int i = 0; i < indent; ++i) { s.PutChar('\t'); }
|
||||
s.PutChar('}'); s.PutChar(' ');
|
||||
node.End(s, false, indent, false);
|
||||
}
|
||||
|
||||
// binary property node from vector of doubles
|
||||
// TODO: optional zip compression!
|
||||
void FBX::Node::WritePropertyNode(
|
||||
void FBX::Node::WritePropertyNodeBinary(
|
||||
const std::string& name,
|
||||
const std::vector<double>& v,
|
||||
Assimp::StreamWriterLE& s
|
||||
){
|
||||
Node node(name);
|
||||
node.Begin(s);
|
||||
FBX::Node node(name);
|
||||
node.BeginBinary(s);
|
||||
s.PutU1('d');
|
||||
s.PutU4(uint32_t(v.size())); // number of elements
|
||||
s.PutU4(0); // no encoding (1 would be zip-compressed)
|
||||
s.PutU4(uint32_t(v.size()) * 8); // data size
|
||||
for (auto it = v.begin(); it != v.end(); ++it) { s.PutF8(*it); }
|
||||
node.EndProperties(s, 1);
|
||||
node.End(s, false);
|
||||
node.EndPropertiesBinary(s, 1);
|
||||
node.EndBinary(s, false);
|
||||
}
|
||||
|
||||
// convenience function to create and write a property node,
|
||||
// holding a single property which is an array of values.
|
||||
// does not copy the data, so is efficient for large arrays.
|
||||
// binary property node from vector of int32_t
|
||||
// TODO: optional zip compression!
|
||||
void FBX::Node::WritePropertyNode(
|
||||
void FBX::Node::WritePropertyNodeBinary(
|
||||
const std::string& name,
|
||||
const std::vector<int32_t>& v,
|
||||
Assimp::StreamWriterLE& s
|
||||
){
|
||||
Node node(name);
|
||||
node.Begin(s);
|
||||
FBX::Node node(name);
|
||||
node.BeginBinary(s);
|
||||
s.PutU1('i');
|
||||
s.PutU4(uint32_t(v.size())); // number of elements
|
||||
s.PutU4(0); // no encoding (1 would be zip-compressed)
|
||||
s.PutU4(uint32_t(v.size()) * 4); // data size
|
||||
for (auto it = v.begin(); it != v.end(); ++it) { s.PutI4(*it); }
|
||||
node.EndProperties(s, 1);
|
||||
node.End(s, false);
|
||||
node.EndPropertiesBinary(s, 1);
|
||||
node.EndBinary(s, false);
|
||||
}
|
||||
|
||||
// public static member functions
|
||||
|
||||
// convenience function to create and write a property node,
|
||||
// holding a single property which is an array of values.
|
||||
// does not copy the data, so is efficient for large arrays.
|
||||
void FBX::Node::WritePropertyNode(
|
||||
const std::string& name,
|
||||
const std::vector<double>& v,
|
||||
Assimp::StreamWriterLE& s,
|
||||
bool binary, int indent
|
||||
){
|
||||
if (binary) {
|
||||
FBX::Node::WritePropertyNodeBinary(name, v, s);
|
||||
} else {
|
||||
FBX::Node::WritePropertyNodeAscii(name, v, s, indent);
|
||||
}
|
||||
}
|
||||
|
||||
// convenience function to create and write a property node,
|
||||
// holding a single property which is an array of values.
|
||||
// does not copy the data, so is efficient for large arrays.
|
||||
void FBX::Node::WritePropertyNode(
|
||||
const std::string& name,
|
||||
const std::vector<int32_t>& v,
|
||||
Assimp::StreamWriterLE& s,
|
||||
bool binary, int indent
|
||||
){
|
||||
if (binary) {
|
||||
FBX::Node::WritePropertyNodeBinary(name, v, s);
|
||||
} else {
|
||||
FBX::Node::WritePropertyNodeAscii(name, v, s, indent);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ASSIMP_BUILD_NO_FBX_EXPORTER
|
||||
#endif // ASSIMP_BUILD_NO_EXPORT
|
||||
|
|
|
@ -142,19 +142,48 @@ public: // support specifically for dealing with Properties70 nodes
|
|||
|
||||
public: // member functions for writing data to a file or stream
|
||||
|
||||
// write the full node as binary data to the given file or stream
|
||||
void Dump(std::shared_ptr<Assimp::IOStream> outfile);
|
||||
void Dump(Assimp::StreamWriterLE &s);
|
||||
// write the full node to the given file or stream
|
||||
void Dump(
|
||||
std::shared_ptr<Assimp::IOStream> outfile,
|
||||
bool binary, int indent
|
||||
);
|
||||
void Dump(Assimp::StreamWriterLE &s, bool binary, int indent);
|
||||
|
||||
// these other functions are for writing data piece by piece.
|
||||
// they must be used carefully.
|
||||
// for usage examples see FBXExporter.cpp.
|
||||
void Begin(Assimp::StreamWriterLE &s);
|
||||
void DumpProperties(Assimp::StreamWriterLE& s);
|
||||
void EndProperties(Assimp::StreamWriterLE &s);
|
||||
void EndProperties(Assimp::StreamWriterLE &s, size_t num_properties);
|
||||
void DumpChildren(Assimp::StreamWriterLE& s);
|
||||
void End(Assimp::StreamWriterLE &s, bool has_children);
|
||||
void Begin(Assimp::StreamWriterLE &s, bool binary, int indent);
|
||||
void DumpProperties(Assimp::StreamWriterLE& s, bool binary, int indent);
|
||||
void EndProperties(Assimp::StreamWriterLE &s, bool binary, int indent);
|
||||
void EndProperties(
|
||||
Assimp::StreamWriterLE &s, bool binary, int indent,
|
||||
size_t num_properties
|
||||
);
|
||||
void BeginChildren(Assimp::StreamWriterLE &s, bool binary, int indent);
|
||||
void DumpChildren(Assimp::StreamWriterLE& s, bool binary, int indent);
|
||||
void End(
|
||||
Assimp::StreamWriterLE &s, bool binary, int indent,
|
||||
bool has_children
|
||||
);
|
||||
|
||||
private: // internal functions used for writing
|
||||
|
||||
void DumpBinary(Assimp::StreamWriterLE &s);
|
||||
void DumpAscii(Assimp::StreamWriterLE &s, int indent);
|
||||
void DumpAscii(std::ostream &s, int indent);
|
||||
|
||||
void BeginBinary(Assimp::StreamWriterLE &s);
|
||||
void DumpPropertiesBinary(Assimp::StreamWriterLE& s);
|
||||
void EndPropertiesBinary(Assimp::StreamWriterLE &s);
|
||||
void EndPropertiesBinary(Assimp::StreamWriterLE &s, size_t num_properties);
|
||||
void DumpChildrenBinary(Assimp::StreamWriterLE& s);
|
||||
void EndBinary(Assimp::StreamWriterLE &s, bool has_children);
|
||||
|
||||
void BeginAscii(std::ostream &s, int indent);
|
||||
void DumpPropertiesAscii(std::ostream &s, int indent);
|
||||
void BeginChildrenAscii(std::ostream &s, int indent);
|
||||
void DumpChildrenAscii(std::ostream &s, int indent);
|
||||
void EndAscii(std::ostream &s, int indent, bool has_children);
|
||||
|
||||
private: // data used for binary dumps
|
||||
size_t start_pos; // starting position in stream
|
||||
|
@ -169,11 +198,12 @@ public: // static member functions
|
|||
static void WritePropertyNode(
|
||||
const std::string& name,
|
||||
const T value,
|
||||
Assimp::StreamWriterLE& s
|
||||
Assimp::StreamWriterLE& s,
|
||||
bool binary, int indent
|
||||
) {
|
||||
FBX::Property p(value);
|
||||
FBX::Node node(name, p);
|
||||
node.Dump(s);
|
||||
node.Dump(s, binary, indent);
|
||||
}
|
||||
|
||||
// convenience function to create and write a property node,
|
||||
|
@ -182,7 +212,8 @@ public: // static member functions
|
|||
static void WritePropertyNode(
|
||||
const std::string& name,
|
||||
const std::vector<double>& v,
|
||||
Assimp::StreamWriterLE& s
|
||||
Assimp::StreamWriterLE& s,
|
||||
bool binary, int indent
|
||||
);
|
||||
|
||||
// convenience function to create and write a property node,
|
||||
|
@ -191,8 +222,34 @@ public: // static member functions
|
|||
static void WritePropertyNode(
|
||||
const std::string& name,
|
||||
const std::vector<int32_t>& v,
|
||||
Assimp::StreamWriterLE& s,
|
||||
bool binary, int indent
|
||||
);
|
||||
|
||||
private: // static helper functions
|
||||
static void WritePropertyNodeAscii(
|
||||
const std::string& name,
|
||||
const std::vector<double>& v,
|
||||
Assimp::StreamWriterLE& s,
|
||||
int indent
|
||||
);
|
||||
static void WritePropertyNodeAscii(
|
||||
const std::string& name,
|
||||
const std::vector<int32_t>& v,
|
||||
Assimp::StreamWriterLE& s,
|
||||
int indent
|
||||
);
|
||||
static void WritePropertyNodeBinary(
|
||||
const std::string& name,
|
||||
const std::vector<double>& v,
|
||||
Assimp::StreamWriterLE& s
|
||||
);
|
||||
static void WritePropertyNodeBinary(
|
||||
const std::string& name,
|
||||
const std::vector<int32_t>& v,
|
||||
Assimp::StreamWriterLE& s
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -48,7 +48,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sstream> // stringstream
|
||||
#include <ostream>
|
||||
#include <locale>
|
||||
#include <sstream> // ostringstream
|
||||
|
||||
|
||||
// constructors for single element properties
|
||||
|
@ -164,18 +166,18 @@ size_t FBX::Property::size()
|
|||
}
|
||||
}
|
||||
|
||||
void FBX::Property::Dump(Assimp::StreamWriterLE &s)
|
||||
void FBX::Property::DumpBinary(Assimp::StreamWriterLE &s)
|
||||
{
|
||||
s.PutU1(type);
|
||||
uint8_t* d;
|
||||
uint8_t* d = data.data();
|
||||
size_t N;
|
||||
switch (type) {
|
||||
case 'C': s.PutU1(*(reinterpret_cast<uint8_t*>(data.data()))); return;
|
||||
case 'Y': s.PutI2(*(reinterpret_cast<int16_t*>(data.data()))); return;
|
||||
case 'I': s.PutI4(*(reinterpret_cast<int32_t*>(data.data()))); return;
|
||||
case 'F': s.PutF4(*(reinterpret_cast<float*>(data.data()))); return;
|
||||
case 'D': s.PutF8(*(reinterpret_cast<double*>(data.data()))); return;
|
||||
case 'L': s.PutI8(*(reinterpret_cast<int64_t*>(data.data()))); return;
|
||||
case 'C': s.PutU1(*(reinterpret_cast<uint8_t*>(d))); return;
|
||||
case 'Y': s.PutI2(*(reinterpret_cast<int16_t*>(d))); return;
|
||||
case 'I': s.PutI4(*(reinterpret_cast<int32_t*>(d))); return;
|
||||
case 'F': s.PutF4(*(reinterpret_cast<float*>(d))); return;
|
||||
case 'D': s.PutF8(*(reinterpret_cast<double*>(d))); return;
|
||||
case 'L': s.PutI8(*(reinterpret_cast<int64_t*>(d))); return;
|
||||
case 'S':
|
||||
case 'R':
|
||||
s.PutU4(uint32_t(data.size()));
|
||||
|
@ -187,7 +189,6 @@ void FBX::Property::Dump(Assimp::StreamWriterLE &s)
|
|||
s.PutU4(0); // no encoding (1 would be zip-compressed)
|
||||
// TODO: compress if large?
|
||||
s.PutU4(uint32_t(data.size())); // data size
|
||||
d = data.data();
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
s.PutI4((reinterpret_cast<int32_t*>(d))[i]);
|
||||
}
|
||||
|
@ -198,7 +199,6 @@ void FBX::Property::Dump(Assimp::StreamWriterLE &s)
|
|||
s.PutU4(0); // no encoding (1 would be zip-compressed)
|
||||
// TODO: compress if large?
|
||||
s.PutU4(uint32_t(data.size())); // data size
|
||||
d = data.data();
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
s.PutI8((reinterpret_cast<int64_t*>(d))[i]);
|
||||
}
|
||||
|
@ -209,7 +209,6 @@ void FBX::Property::Dump(Assimp::StreamWriterLE &s)
|
|||
s.PutU4(0); // no encoding (1 would be zip-compressed)
|
||||
// TODO: compress if large?
|
||||
s.PutU4(uint32_t(data.size())); // data size
|
||||
d = data.data();
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
s.PutF4((reinterpret_cast<float*>(d))[i]);
|
||||
}
|
||||
|
@ -220,18 +219,146 @@ void FBX::Property::Dump(Assimp::StreamWriterLE &s)
|
|||
s.PutU4(0); // no encoding (1 would be zip-compressed)
|
||||
// TODO: compress if large?
|
||||
s.PutU4(uint32_t(data.size())); // data size
|
||||
d = data.data();
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
s.PutF8((reinterpret_cast<double*>(d))[i]);
|
||||
}
|
||||
return;
|
||||
default:
|
||||
std::stringstream err;
|
||||
std::ostringstream err;
|
||||
err << "Tried to dump property with invalid type '";
|
||||
err << type << "'!";
|
||||
throw DeadlyExportError(err.str());
|
||||
}
|
||||
}
|
||||
|
||||
void FBX::Property::DumpAscii(Assimp::StreamWriterLE &outstream, int indent)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
ss.precision(15); // this seems to match official FBX SDK exports
|
||||
DumpAscii(ss, indent);
|
||||
outstream.PutString(ss.str());
|
||||
}
|
||||
|
||||
void FBX::Property::DumpAscii(std::ostream& s, int indent)
|
||||
{
|
||||
// no writing type... or anything. just shove it into the stream.
|
||||
uint8_t* d = data.data();
|
||||
size_t N;
|
||||
size_t swap = data.size();
|
||||
size_t count = 0;
|
||||
switch (type) {
|
||||
case 'C':
|
||||
if (*(reinterpret_cast<uint8_t*>(d))) { s << 'T'; }
|
||||
else { s << 'F'; }
|
||||
return;
|
||||
case 'Y': s << *(reinterpret_cast<int16_t*>(d)); return;
|
||||
case 'I': s << *(reinterpret_cast<int32_t*>(d)); return;
|
||||
case 'F': s << *(reinterpret_cast<float*>(d)); return;
|
||||
case 'D': s << *(reinterpret_cast<double*>(d)); return;
|
||||
case 'L': s << *(reinterpret_cast<int64_t*>(d)); return;
|
||||
case 'S':
|
||||
// first search to see if it has "\x00\x01" in it -
|
||||
// which separates fields which are reversed in the ascii version.
|
||||
// yeah.
|
||||
// FBX, yeah.
|
||||
for (size_t i = 0; i < data.size(); ++i) {
|
||||
if (data[i] == '\0') {
|
||||
swap = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
case 'R':
|
||||
s << '"';
|
||||
// we might as well check this now,
|
||||
// probably it will never happen
|
||||
for (size_t i = 0; i < data.size(); ++i) {
|
||||
char c = data[i];
|
||||
if (c == '"') {
|
||||
throw runtime_error("can't handle quotes in property string");
|
||||
}
|
||||
}
|
||||
// first write the SWAPPED member (if any)
|
||||
for (size_t i = swap + 2; i < data.size(); ++i) {
|
||||
char c = data[i];
|
||||
s << c;
|
||||
}
|
||||
// then a separator
|
||||
if (swap != data.size()) {
|
||||
s << "::";
|
||||
}
|
||||
// then the initial member
|
||||
for (size_t i = 0; i < swap; ++i) {
|
||||
char c = data[i];
|
||||
s << c;
|
||||
}
|
||||
s << '"';
|
||||
return;
|
||||
case 'i':
|
||||
N = data.size() / 4; // number of elements
|
||||
s << '*' << N << " {\n";
|
||||
for (int i = 0; i < indent + 1; ++i) { s << '\t'; }
|
||||
s << "a: ";
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
if (i > 0) { s << ','; }
|
||||
if (count++ > 120) { s << '\n'; count = 0; }
|
||||
s << (reinterpret_cast<int32_t*>(d))[i];
|
||||
}
|
||||
s << '\n';
|
||||
for (int i = 0; i < indent; ++i) { s << '\t'; }
|
||||
s << "} ";
|
||||
return;
|
||||
case 'l':
|
||||
N = data.size() / 8;
|
||||
s << '*' << N << " {\n";
|
||||
for (int i = 0; i < indent + 1; ++i) { s << '\t'; }
|
||||
s << "a: ";
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
if (i > 0) { s << ','; }
|
||||
if (count++ > 120) { s << '\n'; count = 0; }
|
||||
s << (reinterpret_cast<int64_t*>(d))[i];
|
||||
}
|
||||
s << '\n';
|
||||
for (int i = 0; i < indent; ++i) { s << '\t'; }
|
||||
s << "} ";
|
||||
return;
|
||||
case 'f':
|
||||
N = data.size() / 4;
|
||||
s << '*' << N << " {\n";
|
||||
for (int i = 0; i < indent + 1; ++i) { s << '\t'; }
|
||||
s << "a: ";
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
if (i > 0) { s << ','; }
|
||||
if (count++ > 120) { s << '\n'; count = 0; }
|
||||
s << (reinterpret_cast<float*>(d))[i];
|
||||
}
|
||||
s << '\n';
|
||||
for (int i = 0; i < indent; ++i) { s << '\t'; }
|
||||
s << "} ";
|
||||
return;
|
||||
case 'd':
|
||||
N = data.size() / 8;
|
||||
s << '*' << N << " {\n";
|
||||
for (int i = 0; i < indent + 1; ++i) { s << '\t'; }
|
||||
s << "a: ";
|
||||
// set precision to something that can handle doubles
|
||||
s.precision(15);
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
if (i > 0) { s << ','; }
|
||||
if (count++ > 120) { s << '\n'; count = 0; }
|
||||
s << (reinterpret_cast<double*>(d))[i];
|
||||
}
|
||||
s << '\n';
|
||||
for (int i = 0; i < indent; ++i) { s << '\t'; }
|
||||
s << "} ";
|
||||
return;
|
||||
default:
|
||||
std::ostringstream err;
|
||||
err << "Tried to dump property with invalid type '";
|
||||
err << type << "'!";
|
||||
throw runtime_error(err.str());
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ASSIMP_BUILD_NO_FBX_EXPORTER
|
||||
#endif // ASSIMP_BUILD_NO_EXPORT
|
||||
|
|
|
@ -53,6 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <ostream>
|
||||
#include <type_traits> // is_void
|
||||
|
||||
namespace FBX {
|
||||
|
@ -113,7 +114,10 @@ public:
|
|||
size_t size();
|
||||
|
||||
// write this property node as binary data to the given stream
|
||||
void Dump(Assimp::StreamWriterLE &s);
|
||||
void DumpBinary(Assimp::StreamWriterLE &s);
|
||||
void DumpAscii(Assimp::StreamWriterLE &s, int indent=0);
|
||||
void DumpAscii(std::ostream &s, int indent=0);
|
||||
// note: make sure the ostream is in classic "C" locale
|
||||
|
||||
private:
|
||||
char type;
|
||||
|
|
|
@ -66,7 +66,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
#include <vector>
|
||||
#include <array>
|
||||
#include <unordered_set>
|
||||
#include <iostream> // endl
|
||||
|
||||
// RESOURCES:
|
||||
// https://code.blender.org/2013/08/fbx-binary-file-format-specification/
|
||||
|
@ -89,6 +88,8 @@ namespace FBX {
|
|||
"\xfa\xbc\xab\x09\xd0\xc8\xd4\x66\xb1\x76\xfb\x83\x1c\xf7\x26\x7e";
|
||||
const std::string FOOT_MAGIC =
|
||||
"\xf8\x5a\x8c\x6a\xde\xf5\xd9\x7e\xec\xe9\x0c\xe3\x75\x8f\x29\x0b";
|
||||
const std::string COMMENT_UNDERLINE =
|
||||
";------------------------------------------------------------------";
|
||||
}
|
||||
|
||||
using namespace Assimp;
|
||||
|
@ -115,7 +116,7 @@ namespace Assimp {
|
|||
// ---------------------------------------------------------------------
|
||||
// Worker function for exporting a scene to ASCII FBX.
|
||||
// Prototyped and registered in Exporter.cpp
|
||||
/*void ExportSceneFBXA (
|
||||
void ExportSceneFBXA (
|
||||
const char* pFile,
|
||||
IOSystem* pIOSystem,
|
||||
const aiScene* pScene,
|
||||
|
@ -126,7 +127,7 @@ namespace Assimp {
|
|||
|
||||
// perform ascii export
|
||||
exporter.ExportAscii(pFile, pIOSystem);
|
||||
}*/ // TODO
|
||||
}
|
||||
|
||||
} // end of namespace Assimp
|
||||
|
||||
|
@ -194,27 +195,43 @@ void FBXExporter::ExportAscii (
|
|||
);
|
||||
}
|
||||
|
||||
// this isn't really necessary,
|
||||
// but the Autodesk FBX SDK puts a similar comment at the top of the file.
|
||||
// Theirs declares that the file copyright is owned by Autodesk...
|
||||
std::stringstream head;
|
||||
using std::endl;
|
||||
head << "; FBX " << EXPORT_VERSION_STR << " project file" << endl;
|
||||
head << "; Created by the Open Asset Import Library (Assimp)" << endl;
|
||||
head << "; http://assimp.org" << endl;
|
||||
head << "; -------------------------------------------------" << endl;
|
||||
head << endl;
|
||||
const std::string ascii_header = head.str();
|
||||
outfile->Write(ascii_header.c_str(), ascii_header.size(), 1);
|
||||
// write the ascii header
|
||||
WriteAsciiHeader();
|
||||
|
||||
// write all the sections
|
||||
WriteAllNodes();
|
||||
|
||||
// make sure the file ends with a newline.
|
||||
// note: if the file is opened in text mode,
|
||||
// this should do the right cross-platform thing.
|
||||
outfile->Write("\n", 1, 1);
|
||||
|
||||
// explicitly release file pointer,
|
||||
// so we don't have to rely on class destruction.
|
||||
outfile.reset();
|
||||
}
|
||||
|
||||
void FBXExporter::WriteAsciiHeader()
|
||||
{
|
||||
// basically just a comment at the top of the file
|
||||
std::stringstream head;
|
||||
head << "; FBX " << EXPORT_VERSION_STR << " project file\n";
|
||||
head << "; Created by the Open Asset Import Library (Assimp)\n";
|
||||
head << "; http://assimp.org\n";
|
||||
head << "; -------------------------------------------------\n";
|
||||
const std::string ascii_header = head.str();
|
||||
outfile->Write(ascii_header.c_str(), ascii_header.size(), 1);
|
||||
}
|
||||
|
||||
void FBXExporter::WriteAsciiSectionHeader(const std::string& title)
|
||||
{
|
||||
StreamWriterLE outstream(outfile);
|
||||
std::stringstream s;
|
||||
s << "\n\n; " << title << '\n';
|
||||
s << FBX::COMMENT_UNDERLINE << "\n";
|
||||
outstream.PutString(s.str());
|
||||
}
|
||||
|
||||
void FBXExporter::WriteBinaryHeader()
|
||||
{
|
||||
// first a specific sequence of 23 bytes, always the same
|
||||
|
@ -294,28 +311,39 @@ void FBXExporter::WriteAllNodes ()
|
|||
//FBXHeaderExtension top-level node
|
||||
void FBXExporter::WriteHeaderExtension ()
|
||||
{
|
||||
if (!binary) {
|
||||
// no title, follows directly from the top comment
|
||||
}
|
||||
FBX::Node n("FBXHeaderExtension");
|
||||
StreamWriterLE outstream(outfile);
|
||||
int indent = 0;
|
||||
|
||||
// begin node
|
||||
n.Begin(outstream);
|
||||
n.Begin(outstream, binary, indent);
|
||||
|
||||
// write properties
|
||||
// (none)
|
||||
|
||||
// finish properties
|
||||
n.EndProperties(outstream, 0);
|
||||
n.EndProperties(outstream, binary, indent, 0);
|
||||
|
||||
// begin children
|
||||
n.BeginChildren(outstream, binary, indent);
|
||||
|
||||
indent = 1;
|
||||
|
||||
// write child nodes
|
||||
FBX::Node::WritePropertyNode(
|
||||
"FBXHeaderVersion", int32_t(1003), outstream
|
||||
"FBXHeaderVersion", int32_t(1003), outstream, binary, indent
|
||||
);
|
||||
FBX::Node::WritePropertyNode(
|
||||
"FBXVersion", int32_t(EXPORT_VERSION_INT), outstream
|
||||
"FBXVersion", int32_t(EXPORT_VERSION_INT), outstream, binary, indent
|
||||
);
|
||||
if (binary) {
|
||||
FBX::Node::WritePropertyNode(
|
||||
"EncryptionType", int32_t(0), outstream
|
||||
"EncryptionType", int32_t(0), outstream, binary, indent
|
||||
);
|
||||
}
|
||||
|
||||
FBX::Node CreationTimeStamp("CreationTimeStamp");
|
||||
time_t rawtime;
|
||||
|
@ -329,36 +357,50 @@ void FBXExporter::WriteHeaderExtension ()
|
|||
CreationTimeStamp.AddChild("Minute", int32_t(now->tm_min));
|
||||
CreationTimeStamp.AddChild("Second", int32_t(now->tm_sec));
|
||||
CreationTimeStamp.AddChild("Millisecond", int32_t(0));
|
||||
CreationTimeStamp.Dump(outstream);
|
||||
CreationTimeStamp.Dump(outstream, binary, indent);
|
||||
|
||||
std::stringstream creator;
|
||||
creator << "Open Asset Import Library (Assimp) " << aiGetVersionMajor()
|
||||
<< "." << aiGetVersionMinor() << "." << aiGetVersionRevision();
|
||||
FBX::Node::WritePropertyNode("Creator", creator.str(), outstream);
|
||||
FBX::Node::WritePropertyNode(
|
||||
"Creator", creator.str(), outstream, binary, indent
|
||||
);
|
||||
|
||||
FBX::Node sceneinfo("SceneInfo");
|
||||
//FBX::Node sceneinfo("SceneInfo");
|
||||
//sceneinfo.AddProperty("GlobalInfo" + FBX::SEPARATOR + "SceneInfo");
|
||||
// not sure if any of this is actually needed,
|
||||
// so just write an empty node for now.
|
||||
sceneinfo.Dump(outstream);
|
||||
//sceneinfo.Dump(outstream, binary, indent);
|
||||
|
||||
indent = 0;
|
||||
|
||||
// finish node
|
||||
n.End(outstream, true);
|
||||
n.End(outstream, binary, indent, true);
|
||||
|
||||
// that's it for FBXHeaderExtension...
|
||||
if (!binary) { return; }
|
||||
|
||||
// but binary files also need top-level FileID, CreationTime, Creator:
|
||||
std::vector<uint8_t> raw(GENERIC_FILEID.size());
|
||||
for (size_t i = 0; i < GENERIC_FILEID.size(); ++i) {
|
||||
raw[i] = uint8_t(GENERIC_FILEID[i]);
|
||||
}
|
||||
FBX::Node::WritePropertyNode("FileId", raw, outstream);
|
||||
FBX::Node::WritePropertyNode("CreationTime", GENERIC_CTIME, outstream);
|
||||
FBX::Node::WritePropertyNode("Creator", creator.str(), outstream);
|
||||
FBX::Node::WritePropertyNode(
|
||||
"FileId", raw, outstream, binary, indent
|
||||
);
|
||||
FBX::Node::WritePropertyNode(
|
||||
"CreationTime", GENERIC_CTIME, outstream, binary, indent
|
||||
);
|
||||
FBX::Node::WritePropertyNode(
|
||||
"Creator", creator.str(), outstream, binary, indent
|
||||
);
|
||||
}
|
||||
|
||||
void FBXExporter::WriteGlobalSettings ()
|
||||
{
|
||||
if (!binary) {
|
||||
// no title, follows directly from the header extension
|
||||
}
|
||||
FBX::Node gs("GlobalSettings");
|
||||
gs.AddChild("Version", int32_t(1000));
|
||||
|
||||
|
@ -385,11 +427,15 @@ void FBXExporter::WriteGlobalSettings ()
|
|||
p.AddP70int("CurrentTimeMarker", -1);
|
||||
gs.AddChild(p);
|
||||
|
||||
gs.Dump(outfile);
|
||||
gs.Dump(outfile, binary, 0);
|
||||
}
|
||||
|
||||
void FBXExporter::WriteDocuments ()
|
||||
{
|
||||
if (!binary) {
|
||||
WriteAsciiSectionHeader("Documents Description");
|
||||
}
|
||||
|
||||
// not sure what the use of multiple documents would be,
|
||||
// or whether any end-application supports it
|
||||
FBX::Node docs("Documents");
|
||||
|
@ -411,16 +457,19 @@ void FBXExporter::WriteDocuments ()
|
|||
doc.AddChild("RootNode", int64_t(0));
|
||||
|
||||
docs.AddChild(doc);
|
||||
docs.Dump(outfile);
|
||||
docs.Dump(outfile, binary, 0);
|
||||
}
|
||||
|
||||
void FBXExporter::WriteReferences ()
|
||||
{
|
||||
if (!binary) {
|
||||
WriteAsciiSectionHeader("Document References");
|
||||
}
|
||||
// always empty for now.
|
||||
// not really sure what this is for.
|
||||
FBX::Node n("References");
|
||||
n.force_has_children = true;
|
||||
n.Dump(outfile);
|
||||
n.Dump(outfile, binary, 0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -469,9 +518,6 @@ size_t count_images(const aiScene* scene) {
|
|||
}
|
||||
}
|
||||
}
|
||||
//for (auto &s : images) {
|
||||
// std::cout << "found image: " << s << std::endl;
|
||||
//}
|
||||
return images.size();
|
||||
}
|
||||
|
||||
|
@ -511,6 +557,11 @@ void FBXExporter::WriteDefinitions ()
|
|||
// determining how many of each type of object there are
|
||||
// and specifying the base properties to use when otherwise unspecified.
|
||||
|
||||
// ascii section header
|
||||
if (!binary) {
|
||||
WriteAsciiSectionHeader("Object definitions");
|
||||
}
|
||||
|
||||
// we need to count the objects
|
||||
int32_t count;
|
||||
int32_t total_count = 0;
|
||||
|
@ -890,7 +941,7 @@ void FBXExporter::WriteDefinitions ()
|
|||
defs.AddChild("Version", int32_t(100));
|
||||
defs.AddChild("Count", int32_t(total_count));
|
||||
for (auto &n : object_nodes) { defs.AddChild(n); }
|
||||
defs.Dump(outfile);
|
||||
defs.Dump(outfile, binary, 0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -936,14 +987,20 @@ int64_t to_ktime(double ticks, const aiAnimation* anim) {
|
|||
|
||||
void FBXExporter::WriteObjects ()
|
||||
{
|
||||
if (!binary) {
|
||||
WriteAsciiSectionHeader("Object properties");
|
||||
}
|
||||
// numbers should match those given in definitions! make sure to check
|
||||
StreamWriterLE outstream(outfile);
|
||||
FBX::Node object_node("Objects");
|
||||
object_node.Begin(outstream);
|
||||
object_node.EndProperties(outstream);
|
||||
int indent = 0;
|
||||
object_node.Begin(outstream, binary, indent);
|
||||
object_node.EndProperties(outstream, binary, indent);
|
||||
object_node.BeginChildren(outstream, binary, indent);
|
||||
|
||||
// geometry (aiMesh)
|
||||
mesh_uids.clear();
|
||||
indent = 1;
|
||||
for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) {
|
||||
// it's all about this mesh
|
||||
aiMesh* m = mScene->mMeshes[mi];
|
||||
|
@ -955,9 +1012,11 @@ void FBXExporter::WriteObjects ()
|
|||
n.AddProperty(uid);
|
||||
n.AddProperty(FBX::SEPARATOR + "Geometry");
|
||||
n.AddProperty("Mesh");
|
||||
n.Begin(outstream);
|
||||
n.DumpProperties(outstream);
|
||||
n.EndProperties(outstream);
|
||||
n.Begin(outstream, binary, indent);
|
||||
n.DumpProperties(outstream, binary, indent);
|
||||
n.EndProperties(outstream, binary, indent);
|
||||
n.BeginChildren(outstream, binary, indent);
|
||||
indent = 2;
|
||||
|
||||
// output vertex data - each vertex should be unique (probably)
|
||||
std::vector<double> flattened_vertices;
|
||||
|
@ -981,7 +1040,7 @@ void FBXExporter::WriteObjects ()
|
|||
}
|
||||
}
|
||||
FBX::Node::WritePropertyNode(
|
||||
"Vertices", flattened_vertices, outstream
|
||||
"Vertices", flattened_vertices, outstream, binary, indent
|
||||
);
|
||||
|
||||
// output polygon data as a flattened array of vertex indices.
|
||||
|
@ -997,30 +1056,38 @@ void FBXExporter::WriteObjects ()
|
|||
);
|
||||
}
|
||||
FBX::Node::WritePropertyNode(
|
||||
"PolygonVertexIndex", polygon_data, outstream
|
||||
"PolygonVertexIndex", polygon_data, outstream, binary, indent
|
||||
);
|
||||
|
||||
// here could be edges but they're insane.
|
||||
// it's optional anyway, so let's ignore it.
|
||||
|
||||
FBX::Node::WritePropertyNode(
|
||||
"GeometryVersion", int32_t(124), outstream
|
||||
"GeometryVersion", int32_t(124), outstream, binary, indent
|
||||
);
|
||||
|
||||
// normals, if any
|
||||
if (m->HasNormals()) {
|
||||
FBX::Node normals("LayerElementNormal", int32_t(0));
|
||||
normals.Begin(outstream);
|
||||
normals.DumpProperties(outstream);
|
||||
normals.EndProperties(outstream);
|
||||
FBX::Node::WritePropertyNode("Version", int32_t(101), outstream);
|
||||
FBX::Node::WritePropertyNode("Name", "", outstream);
|
||||
normals.Begin(outstream, binary, indent);
|
||||
normals.DumpProperties(outstream, binary, indent);
|
||||
normals.EndProperties(outstream, binary, indent);
|
||||
normals.BeginChildren(outstream, binary, indent);
|
||||
indent = 3;
|
||||
FBX::Node::WritePropertyNode(
|
||||
"MappingInformationType", "ByPolygonVertex", outstream
|
||||
"Version", int32_t(101), outstream, binary, indent
|
||||
);
|
||||
FBX::Node::WritePropertyNode(
|
||||
"Name", "", outstream, binary, indent
|
||||
);
|
||||
FBX::Node::WritePropertyNode(
|
||||
"MappingInformationType", "ByPolygonVertex",
|
||||
outstream, binary, indent
|
||||
);
|
||||
// TODO: vertex-normals or indexed normals when appropriate
|
||||
FBX::Node::WritePropertyNode(
|
||||
"ReferenceInformationType", "Direct", outstream
|
||||
"ReferenceInformationType", "Direct",
|
||||
outstream, binary, indent
|
||||
);
|
||||
std::vector<double> normal_data;
|
||||
normal_data.reserve(3 * polygon_data.size());
|
||||
|
@ -1033,10 +1100,13 @@ void FBXExporter::WriteObjects ()
|
|||
normal_data.push_back(n.z);
|
||||
}
|
||||
}
|
||||
FBX::Node::WritePropertyNode("Normals", normal_data, outstream);
|
||||
FBX::Node::WritePropertyNode(
|
||||
"Normals", normal_data, outstream, binary, indent
|
||||
);
|
||||
// note: version 102 has a NormalsW also... not sure what it is,
|
||||
// so we can stick with version 101 for now.
|
||||
normals.End(outstream, true);
|
||||
indent = 2;
|
||||
normals.End(outstream, binary, indent, true);
|
||||
}
|
||||
|
||||
// uvs, if any
|
||||
|
@ -1057,18 +1127,26 @@ void FBXExporter::WriteObjects ()
|
|||
DefaultLogger::get()->warn(err.str());
|
||||
}
|
||||
FBX::Node uv("LayerElementUV", int32_t(uvi));
|
||||
uv.Begin(outstream);
|
||||
uv.DumpProperties(outstream);
|
||||
uv.EndProperties(outstream);
|
||||
FBX::Node::WritePropertyNode("Version", int32_t(101), outstream);
|
||||
uv.Begin(outstream, binary, indent);
|
||||
uv.DumpProperties(outstream, binary, indent);
|
||||
uv.EndProperties(outstream, binary, indent);
|
||||
uv.BeginChildren(outstream, binary, indent);
|
||||
indent = 3;
|
||||
FBX::Node::WritePropertyNode(
|
||||
"Version", int32_t(101), outstream, binary, indent
|
||||
);
|
||||
// it doesn't seem like assimp keeps the uv map name,
|
||||
// so just leave it blank.
|
||||
FBX::Node::WritePropertyNode("Name", "", outstream);
|
||||
FBX::Node::WritePropertyNode(
|
||||
"MappingInformationType", "ByPolygonVertex", outstream
|
||||
"Name", "", outstream, binary, indent
|
||||
);
|
||||
FBX::Node::WritePropertyNode(
|
||||
"ReferenceInformationType", "IndexToDirect", outstream
|
||||
"MappingInformationType", "ByPolygonVertex",
|
||||
outstream, binary, indent
|
||||
);
|
||||
FBX::Node::WritePropertyNode(
|
||||
"ReferenceInformationType", "IndexToDirect",
|
||||
outstream, binary, indent
|
||||
);
|
||||
|
||||
std::vector<double> uv_data;
|
||||
|
@ -1093,9 +1171,14 @@ void FBXExporter::WriteObjects ()
|
|||
}
|
||||
}
|
||||
}
|
||||
FBX::Node::WritePropertyNode("UV", uv_data, outstream);
|
||||
FBX::Node::WritePropertyNode("UVIndex", uv_indices, outstream);
|
||||
uv.End(outstream, true);
|
||||
FBX::Node::WritePropertyNode(
|
||||
"UV", uv_data, outstream, binary, indent
|
||||
);
|
||||
FBX::Node::WritePropertyNode(
|
||||
"UVIndex", uv_indices, outstream, binary, indent
|
||||
);
|
||||
indent = 2;
|
||||
uv.End(outstream, binary, indent, true);
|
||||
}
|
||||
|
||||
// i'm not really sure why this material section exists,
|
||||
|
@ -1108,7 +1191,7 @@ void FBXExporter::WriteObjects ()
|
|||
mat.AddChild("ReferenceInformationType", "IndexToDirect");
|
||||
std::vector<int32_t> mat_indices = {0};
|
||||
mat.AddChild("Materials", mat_indices);
|
||||
mat.Dump(outstream);
|
||||
mat.Dump(outstream, binary, indent);
|
||||
|
||||
// finally we have the layer specifications,
|
||||
// which select the normals / UV set / etc to use.
|
||||
|
@ -1127,10 +1210,11 @@ void FBXExporter::WriteObjects ()
|
|||
le.AddChild("Type", "LayerElementUV");
|
||||
le.AddChild("TypedIndex", int32_t(0));
|
||||
layer.AddChild(le);
|
||||
layer.Dump(outstream);
|
||||
layer.Dump(outstream, binary, indent);
|
||||
|
||||
// finish the node record
|
||||
n.End(outstream, true);
|
||||
indent = 1;
|
||||
n.End(outstream, binary, indent, true);
|
||||
}
|
||||
|
||||
// aiMaterial
|
||||
|
@ -1274,7 +1358,7 @@ void FBXExporter::WriteObjects ()
|
|||
|
||||
n.AddChild(p);
|
||||
|
||||
n.Dump(outstream);
|
||||
n.Dump(outstream, binary, indent);
|
||||
}
|
||||
|
||||
// we need to look up all the images we're using,
|
||||
|
@ -1322,7 +1406,7 @@ void FBXExporter::WriteObjects ()
|
|||
n.AddChild("UseMipMap", int32_t(0));
|
||||
n.AddChild("Filename", path);
|
||||
n.AddChild("RelativeFilename", path);
|
||||
n.Dump(outstream);
|
||||
n.Dump(outstream, binary, indent);
|
||||
}
|
||||
|
||||
// Textures
|
||||
|
@ -1441,7 +1525,7 @@ void FBXExporter::WriteObjects ()
|
|||
tnode.AddChild(
|
||||
"Cropping", int32_t(0), int32_t(0), int32_t(0), int32_t(0)
|
||||
);
|
||||
tnode.Dump(outstream);
|
||||
tnode.Dump(outstream, binary, indent);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1594,7 +1678,7 @@ void FBXExporter::WriteObjects ()
|
|||
// "acuracy"... this is not a typo....
|
||||
dnode.AddChild("Link_DeformAcuracy", double(50));
|
||||
dnode.AddChild("SkinningType", "Linear"); // TODO: other modes?
|
||||
dnode.Dump(outstream);
|
||||
dnode.Dump(outstream, binary, indent);
|
||||
|
||||
// connect it
|
||||
connections.emplace_back("C", "OO", deformer_uid, mesh_uids[mi]);
|
||||
|
@ -1738,7 +1822,7 @@ void FBXExporter::WriteObjects ()
|
|||
// there's not really any way around this at the moment.
|
||||
|
||||
// done
|
||||
sdnode.Dump(outstream);
|
||||
sdnode.Dump(outstream, binary, indent);
|
||||
|
||||
// lastly, connect to the parent deformer
|
||||
connections.emplace_back(
|
||||
|
@ -1856,7 +1940,7 @@ void FBXExporter::WriteObjects ()
|
|||
}
|
||||
|
||||
// now write it
|
||||
bpnode.Dump(outstream);
|
||||
bpnode.Dump(outstream, binary, indent);
|
||||
}*/
|
||||
|
||||
// TODO: cameras, lights
|
||||
|
@ -1916,7 +2000,7 @@ void FBXExporter::WriteObjects ()
|
|||
// this node absurdly always pretends it has children
|
||||
// (in this case it does, but just in case...)
|
||||
asnode.force_has_children = true;
|
||||
asnode.Dump(outstream);
|
||||
asnode.Dump(outstream, binary, indent);
|
||||
|
||||
// note: animation stacks are not connected to anything
|
||||
}
|
||||
|
@ -1931,7 +2015,7 @@ void FBXExporter::WriteObjects ()
|
|||
|
||||
// this node absurdly always pretends it has children
|
||||
alnode.force_has_children = true;
|
||||
alnode.Dump(outstream);
|
||||
alnode.Dump(outstream, binary, indent);
|
||||
|
||||
// connect to the relevant animstack
|
||||
connections.emplace_back(
|
||||
|
@ -2048,7 +2132,8 @@ void FBXExporter::WriteObjects ()
|
|||
}
|
||||
}
|
||||
|
||||
object_node.End(outstream, true);
|
||||
indent = 0;
|
||||
object_node.End(outstream, binary, indent, true);
|
||||
}
|
||||
|
||||
// convenience map of magic node name strings to FBX properties,
|
||||
|
@ -2074,13 +2159,14 @@ const std::map<std::string,std::pair<std::string,char>> transform_types = {
|
|||
};
|
||||
|
||||
// write a single model node to the stream
|
||||
void WriteModelNode(
|
||||
void FBXExporter::WriteModelNode(
|
||||
StreamWriterLE& outstream,
|
||||
bool binary,
|
||||
const aiNode* node,
|
||||
int64_t node_uid,
|
||||
const std::string& type,
|
||||
const std::vector<std::pair<std::string,aiVector3D>>& transform_chain,
|
||||
TransformInheritance inherit_type=TransformInheritance_RSrs
|
||||
TransformInheritance inherit_type
|
||||
){
|
||||
const aiVector3D zero = {0, 0, 0};
|
||||
const aiVector3D one = {1, 1, 1};
|
||||
|
@ -2148,7 +2234,7 @@ void WriteModelNode(
|
|||
m.AddChild("Shading", Property(true));
|
||||
m.AddChild("Culling", Property("CullingOff"));
|
||||
|
||||
m.Dump(outstream);
|
||||
m.Dump(outstream, binary, 1);
|
||||
}
|
||||
|
||||
// wrapper for WriteModelNodes to create and pass a blank transform chain
|
||||
|
@ -2249,9 +2335,13 @@ void FBXExporter::WriteModelNodes(
|
|||
node_uid
|
||||
);
|
||||
// write model node
|
||||
WriteModelNode(outstream, node, node_uid, "Mesh", transform_chain);
|
||||
WriteModelNode(
|
||||
outstream, binary, node, node_uid, "Mesh", transform_chain
|
||||
);
|
||||
} else if (limbnodes.count(node)) {
|
||||
WriteModelNode(outstream, node, node_uid, "LimbNode", transform_chain);
|
||||
WriteModelNode(
|
||||
outstream, binary, node, node_uid, "LimbNode", transform_chain
|
||||
);
|
||||
// we also need to write a nodeattribute to mark it as a skeleton
|
||||
int64_t node_attribute_uid = generate_uid();
|
||||
FBX::Node na("NodeAttribute");
|
||||
|
@ -2259,12 +2349,14 @@ void FBXExporter::WriteModelNodes(
|
|||
node_attribute_uid, FBX::SEPARATOR + "NodeAttribute", "LimbNode"
|
||||
);
|
||||
na.AddChild("TypeFlags", Property("Skeleton"));
|
||||
na.Dump(outstream);
|
||||
na.Dump(outstream, binary, 1);
|
||||
// and connect them
|
||||
connections.emplace_back("C", "OO", node_attribute_uid, node_uid);
|
||||
} else {
|
||||
// generate a null node so we can add children to it
|
||||
WriteModelNode(outstream, node, node_uid, "Null", transform_chain);
|
||||
WriteModelNode(
|
||||
outstream, binary, node, node_uid, "Null", transform_chain
|
||||
);
|
||||
}
|
||||
|
||||
// if more than one child mesh, make nodes for each mesh
|
||||
|
@ -2296,7 +2388,7 @@ void FBXExporter::WriteModelNodes(
|
|||
FBX::Node p("Properties70");
|
||||
p.AddP70enum("InheritType", 1);
|
||||
m.AddChild(p);
|
||||
m.Dump(outstream);
|
||||
m.Dump(outstream, binary, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2325,7 +2417,7 @@ void FBXExporter::WriteAnimationCurveNode(
|
|||
p.AddP70numberA("d|Y", default_value.y);
|
||||
p.AddP70numberA("d|Z", default_value.z);
|
||||
n.AddChild(p);
|
||||
n.Dump(outstream);
|
||||
n.Dump(outstream, binary, 1);
|
||||
// connect to layer
|
||||
this->connections.emplace_back("C", "OO", uid, layer_uid);
|
||||
// connect to bone
|
||||
|
@ -2356,7 +2448,7 @@ void FBXExporter::WriteAnimationCurve(
|
|||
"KeyAttrRefCount",
|
||||
std::vector<int32_t>{static_cast<int32_t>(times.size())}
|
||||
);
|
||||
n.Dump(outstream);
|
||||
n.Dump(outstream, binary, 1);
|
||||
this->connections.emplace_back(
|
||||
"C", "OP", curve_uid, curvenode_uid, property_link
|
||||
);
|
||||
|
@ -2367,13 +2459,18 @@ void FBXExporter::WriteConnections ()
|
|||
{
|
||||
// we should have completed the connection graph already,
|
||||
// so basically just dump it here
|
||||
if (!binary) {
|
||||
WriteAsciiSectionHeader("Object connections");
|
||||
}
|
||||
// TODO: comments with names in the ascii version
|
||||
FBX::Node conn("Connections");
|
||||
StreamWriterLE outstream(outfile);
|
||||
conn.Begin(outstream);
|
||||
conn.Begin(outstream, binary, 0);
|
||||
conn.BeginChildren(outstream, binary, 0);
|
||||
for (auto &n : connections) {
|
||||
n.Dump(outstream);
|
||||
n.Dump(outstream, binary, 1);
|
||||
}
|
||||
conn.End(outstream, !connections.empty());
|
||||
conn.End(outstream, binary, 0, !connections.empty());
|
||||
connections.clear();
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
|
||||
|
||||
#include "FBXExportNode.h" // FBX::Node
|
||||
#include "FBXCommon.h" // FBX::TransformInheritance
|
||||
|
||||
#include <assimp/types.h>
|
||||
//#include <assimp/material.h>
|
||||
|
@ -104,6 +105,9 @@ namespace Assimp
|
|||
void WriteBinaryHeader();
|
||||
void WriteBinaryFooter();
|
||||
|
||||
// ascii files have a comment at the top
|
||||
void WriteAsciiHeader();
|
||||
|
||||
// WriteAllNodes does the actual export.
|
||||
// It just calls all the Write<Section> methods below in order.
|
||||
void WriteAllNodes();
|
||||
|
@ -126,6 +130,7 @@ namespace Assimp
|
|||
// WriteTakes(); // deprecated since at least 2015 (fbx 7.4)
|
||||
|
||||
// helpers
|
||||
void WriteAsciiSectionHeader(const std::string& title);
|
||||
void WriteModelNodes(
|
||||
Assimp::StreamWriterLE& s,
|
||||
const aiNode* node,
|
||||
|
@ -139,6 +144,15 @@ namespace Assimp
|
|||
const std::unordered_set<const aiNode*>& limbnodes,
|
||||
std::vector<std::pair<std::string,aiVector3D>>& transform_chain
|
||||
);
|
||||
void WriteModelNode( // nor this
|
||||
StreamWriterLE& s,
|
||||
bool binary,
|
||||
const aiNode* node,
|
||||
int64_t node_uid,
|
||||
const std::string& type,
|
||||
const std::vector<std::pair<std::string,aiVector3D>>& xfm_chain,
|
||||
FBX::TransformInheritance ti_type=FBX::TransformInheritance_RSrs
|
||||
);
|
||||
void WriteAnimationCurveNode(
|
||||
StreamWriterLE& outstream,
|
||||
int64_t uid,
|
||||
|
|
Loading…
Reference in New Issue