Merge branch 'master' into develop_kimkulling

pull/1871/head
Kim Kulling 2018-04-04 16:32:46 +02:00
commit 0e945b5581
12 changed files with 866 additions and 317 deletions

View File

@ -35,29 +35,29 @@ Please check our Wiki as well: https://github.com/assimp/assimp/wiki
__Importers__: __Importers__:
- 3D - 3D
- 3DS - [3DS](https://en.wikipedia.org/wiki/.3ds)
- 3MF - [3MF](https://en.wikipedia.org/wiki/3D_Manufacturing_Format)
- AC - AC
- AC3D - [AC3D](https://en.wikipedia.org/wiki/AC3D)
- ACC - ACC
- AMJ - AMJ
- ASE - ASE
- ASK - ASK
- B3D - B3D
- BLEND (Blender) - [BLEND](https://en.wikipedia.org/wiki/.blend_(file_format))
- BVH - [BVH](https://en.wikipedia.org/wiki/Biovision_Hierarchy)
- COB
- CMS - CMS
- DAE/Collada - COB
- DXF - [DAE/Collada](https://en.wikipedia.org/wiki/COLLADA)
- [DXF](https://en.wikipedia.org/wiki/AutoCAD_DXF)
- ENFF - ENFF
- FBX - [FBX](https://en.wikipedia.org/wiki/FBX)
- glTF 1.0 + GLB - [glTF 1.0](https://en.wikipedia.org/wiki/GlTF#glTF_1.0) + GLB
- glTF 2.0 - [glTF 2.0](https://en.wikipedia.org/wiki/GlTF#glTF_2.0)
- HMB - HMB
- IFC-STEP - IFC-STEP
- IRR / IRRMESH - IRR / IRRMESH
- LWO - [LWO](https://en.wikipedia.org/wiki/LightWave_3D)
- LWS - LWS
- LXO - LXO
- MD2 - MD2
@ -70,10 +70,10 @@ __Importers__:
- MS3D - MS3D
- NDO - NDO
- NFF - NFF
- OBJ - [OBJ](https://en.wikipedia.org/wiki/Wavefront_.obj_file)
- OFF - [OFF](https://en.wikipedia.org/wiki/OFF_(file_format))
- OGEX - [OGEX](https://en.wikipedia.org/wiki/Open_Game_Engine_Exchange)
- PLY - [PLY](https://en.wikipedia.org/wiki/PLY_(file_format))
- PMX - PMX
- PRJ - PRJ
- Q3O - Q3O
@ -82,19 +82,19 @@ __Importers__:
- SCN - SCN
- SIB - SIB
- SMD - SMD
- STL - [STP](https://en.wikipedia.org/wiki/ISO_10303-21)
- STP - [STL](https://en.wikipedia.org/wiki/STL_(file_format))
- TER - TER
- UC - UC
- VTA - VTA
- X - X
- X3D - [X3D](https://en.wikipedia.org/wiki/X3D)
- XGL - XGL
- ZGL - ZGL
Additionally, some formats are supported by dependency on non-free code or external SDKs (not built by default): Additionally, some formats are supported by dependency on non-free code or external SDKs (not built by default):
- C4D (https://github.com/assimp/assimp/wiki/Cinema4D-&-Melange) - [C4D](https://en.wikipedia.org/wiki/Cinema_4D) (https://github.com/assimp/assimp/wiki/Cinema4D-&-Melange)
__Exporters__: __Exporters__:

View File

@ -34,36 +34,18 @@ if( MSVC )
else() else()
set(MSVC_PREFIX "vc150") set(MSVC_PREFIX "vc150")
endif() endif()
set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@-${MSVC_PREFIX}-mt" CACHE STRING "the suffix for the assimp windows library" FORCE) set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@-${MSVC_PREFIX}-mt" CACHE STRING "the suffix for the assimp windows library" )
else() else()
set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@" CACHE STRING "the suffix for the openrave libraries" FORCE) set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@" CACHE STRING "the suffix for the openrave libraries" )
endif() endif()
set( ASSIMP_CXX_FLAGS ) # dynamically linked library set( ASSIMP_CXX_FLAGS ) # dynamically linked library
if( WIN32 )
# for visual studio linking, most of the time boost dlls will be used
set( ASSIMP_CXX_FLAGS " -DBOOST_ALL_DYN_LINK -DBOOST_ALL_NO_LIB")
endif()
set( ASSIMP_LINK_FLAGS "" ) set( ASSIMP_LINK_FLAGS "" )
set( ASSIMP_LIBRARY_DIRS "${ASSIMP_ROOT_DIR}/@ASSIMP_LIB_INSTALL_DIR@") set( ASSIMP_LIBRARY_DIRS "${ASSIMP_ROOT_DIR}/@ASSIMP_LIB_INSTALL_DIR@")
set( ASSIMP_INCLUDE_DIRS "${ASSIMP_ROOT_DIR}/@ASSIMP_INCLUDE_INSTALL_DIR@") set( ASSIMP_INCLUDE_DIRS "${ASSIMP_ROOT_DIR}/@ASSIMP_INCLUDE_INSTALL_DIR@")
set( ASSIMP_LIBRARIES assimp${ASSIMP_LIBRARY_SUFFIX}) set( ASSIMP_LIBRARIES assimp${ASSIMP_LIBRARY_SUFFIX})
set( ASSIMP_LIBRARIES ${ASSIMP_LIBRARIES}@CMAKE_DEBUG_POSTFIX@) set( ASSIMP_LIBRARIES ${ASSIMP_LIBRARIES}@CMAKE_DEBUG_POSTFIX@)
# search for the boost version assimp was compiled with
#set(Boost_USE_MULTITHREAD ON)
#set(Boost_USE_STATIC_LIBS OFF)
#set(Boost_USE_STATIC_RUNTIME OFF)
#find_package(Boost ${ASSIMP_Boost_VERSION} EXACT COMPONENTS thread date_time)
#if(Boost_VERSION AND NOT "${Boost_VERSION}" STREQUAL "0")
# set( ASSIMP_INCLUDE_DIRS "${ASSIMP_INCLUDE_DIRS}" ${Boost_INCLUDE_DIRS})
#else(Boost_VERSION AND NOT "${Boost_VERSION}" STREQUAL "0")
# message(WARNING "Failed to find Boost ${ASSIMP_Boost_VERSION} necessary for assimp")
#endif(Boost_VERSION AND NOT "${Boost_VERSION}" STREQUAL "0")
# the boost version assimp was compiled with
set( ASSIMP_Boost_VERSION "@Boost_MAJOR_VERSION@.@Boost_MINOR_VERSION@")
# for compatibility with pkg-config # for compatibility with pkg-config
set(ASSIMP_CFLAGS_OTHER "${ASSIMP_CXX_FLAGS}") set(ASSIMP_CFLAGS_OTHER "${ASSIMP_CXX_FLAGS}")
set(ASSIMP_LDFLAGS_OTHER "${ASSIMP_LINK_FLAGS}") set(ASSIMP_LDFLAGS_OTHER "${ASSIMP_LINK_FLAGS}")
@ -74,7 +56,6 @@ MARK_AS_ADVANCED(
ASSIMP_LINK_FLAGS ASSIMP_LINK_FLAGS
ASSIMP_INCLUDE_DIRS ASSIMP_INCLUDE_DIRS
ASSIMP_LIBRARIES ASSIMP_LIBRARIES
ASSIMP_Boost_VERSION
ASSIMP_CFLAGS_OTHER ASSIMP_CFLAGS_OTHER
ASSIMP_LDFLAGS_OTHER ASSIMP_LDFLAGS_OTHER
ASSIMP_LIBRARY_SUFFIX ASSIMP_LIBRARY_SUFFIX

View File

@ -47,11 +47,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef ASSIMP_BUILD_NO_COB_IMPORTER #ifndef ASSIMP_BUILD_NO_COB_IMPORTER
#include "COBLoader.h" #include "COBLoader.h"
#include "COBScene.h" #include "COBScene.h"
#include "ConvertToLHProcess.h"
#include <assimp/StreamReader.h> #include <assimp/StreamReader.h>
#include <assimp/ParsingUtils.h> #include <assimp/ParsingUtils.h>
#include <assimp/fast_atof.h> #include <assimp/fast_atof.h>
#include <assimp/LineSplitter.h> #include <assimp/LineSplitter.h>
#include <assimp/TinyFormatter.h> #include <assimp/TinyFormatter.h>
#include <memory> #include <memory>
@ -105,7 +104,7 @@ COBImporter::~COBImporter()
bool COBImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const bool COBImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
{ {
const std::string& extension = GetExtension(pFile); const std::string& extension = GetExtension(pFile);
if (extension == "cob" || extension == "scn") { if (extension == "cob" || extension == "scn" || extension == "COB" || extension == "SCN") {
return true; return true;
} }
@ -225,6 +224,9 @@ void COBImporter::InternReadFile( const std::string& pFile,
} }
pScene->mRootNode = BuildNodes(*root.get(),scene,pScene); pScene->mRootNode = BuildNodes(*root.get(),scene,pScene);
//flip normals after import
FlipWindingOrderProcess flip;
flip.Execute( pScene );
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -1299,3 +1301,4 @@ void COBImporter::ReadUnit_Binary(COB::Scene& out, StreamReaderLE& reader, const
#endif #endif

View File

@ -98,7 +98,7 @@ void ExportSceneAssbin(const char*, IOSystem*, const aiScene*, const ExportPrope
void ExportSceneAssxml(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneAssxml(const char*, IOSystem*, const aiScene*, const ExportProperties*);
void ExportSceneX3D(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 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* ); void ExportScene3MF( const char*, IOSystem*, const aiScene*, const ExportProperties* );
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -173,7 +173,7 @@ Exporter::ExportFormatEntry gExporters[] =
#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER #ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
Exporter::ExportFormatEntry( "fbx", "Autodesk FBX (binary)", "fbx", &ExportSceneFBX, 0 ), 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 #endif
#ifndef ASSIMP_BUILD_NO_3MF_EXPORTER #ifndef ASSIMP_BUILD_NO_3MF_EXPORTER

View File

@ -45,9 +45,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "FBXCommon.h" #include "FBXCommon.h"
#include <assimp/StreamWriter.h> // StreamWriterLE #include <assimp/StreamWriter.h> // StreamWriterLE
#include <assimp/Exceptional.h> // DeadlyExportError
#include <assimp/ai_assert.h> #include <assimp/ai_assert.h>
#include <assimp/StringUtils.h> // ai_snprintf
#include <string> #include <string>
#include <ostream>
#include <sstream> // ostringstream
#include <memory> // shared_ptr #include <memory> // shared_ptr
// AddP70<type> helpers... there's no usable pattern here, // AddP70<type> helpers... there's no usable pattern here,
@ -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 // public member functions for writing to binary fbx
void FBX::Node::Dump(std::shared_ptr<Assimp::IOStream> outfile) void FBX::Node::DumpBinary(Assimp::StreamWriterLE &s)
{
Assimp::StreamWriterLE outstream(outfile);
Dump(outstream);
}
void FBX::Node::Dump(Assimp::StreamWriterLE &s)
{ {
// write header section (with placeholders for some things) // write header section (with placeholders for some things)
Begin(s); BeginBinary(s);
// write properties // write properties
DumpProperties(s); DumpPropertiesBinary(s);
// go back and fill in property related placeholders // go back and fill in property related placeholders
EndProperties(s, properties.size()); EndPropertiesBinary(s, properties.size());
// write children // write children
DumpChildren(s); DumpChildrenBinary(s);
// finish, filling in end offset placeholder // finish, filling in end offset placeholder
End(s, !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 // remember start pos so we can come back and write the end pos
this->start_pos = s.Tell(); this->start_pos = s.Tell();
@ -189,26 +334,14 @@ void FBX::Node::Begin(Assimp::StreamWriterLE &s)
this->property_start = s.Tell(); this->property_start = s.Tell();
} }
void FBX::Node::DumpProperties(Assimp::StreamWriterLE& s) void FBX::Node::DumpPropertiesBinary(Assimp::StreamWriterLE& s)
{ {
for (auto &p : properties) { for (auto &p : properties) {
p.Dump(s); p.DumpBinary(s);
} }
} }
void FBX::Node::DumpChildren(Assimp::StreamWriterLE& s) void FBX::Node::EndPropertiesBinary(
{
for (FBX::Node& child : children) {
child.Dump(s);
}
}
void FBX::Node::EndProperties(Assimp::StreamWriterLE &s)
{
EndProperties(s, properties.size());
}
void FBX::Node::EndProperties(
Assimp::StreamWriterLE &s, Assimp::StreamWriterLE &s,
size_t num_properties size_t num_properties
) { ) {
@ -222,7 +355,14 @@ void FBX::Node::EndProperties(
s.Seek(pos); 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, Assimp::StreamWriterLE &s,
bool has_children 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, void FBX::Node::DumpPropertiesAscii(std::ostream &s, int indent)
// holding a single property which is an array of values. {
// does not copy the data, so is efficient for large arrays. 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 = ai_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 = ai_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! // TODO: optional zip compression!
void FBX::Node::WritePropertyNode( void FBX::Node::WritePropertyNodeBinary(
const std::string& name, const std::string& name,
const std::vector<double>& v, const std::vector<double>& v,
Assimp::StreamWriterLE& s Assimp::StreamWriterLE& s
){ ){
Node node(name); FBX::Node node(name);
node.Begin(s); node.BeginBinary(s);
s.PutU1('d'); s.PutU1('d');
s.PutU4(uint32_t(v.size())); // number of elements s.PutU4(uint32_t(v.size())); // number of elements
s.PutU4(0); // no encoding (1 would be zip-compressed) s.PutU4(0); // no encoding (1 would be zip-compressed)
s.PutU4(uint32_t(v.size()) * 8); // data size s.PutU4(uint32_t(v.size()) * 8); // data size
for (auto it = v.begin(); it != v.end(); ++it) { s.PutF8(*it); } for (auto it = v.begin(); it != v.end(); ++it) { s.PutF8(*it); }
node.EndProperties(s, 1); node.EndPropertiesBinary(s, 1);
node.End(s, false); node.EndBinary(s, false);
} }
// convenience function to create and write a property node, // binary property node from vector of int32_t
// holding a single property which is an array of values.
// does not copy the data, so is efficient for large arrays.
// TODO: optional zip compression! // TODO: optional zip compression!
void FBX::Node::WritePropertyNode( void FBX::Node::WritePropertyNodeBinary(
const std::string& name, const std::string& name,
const std::vector<int32_t>& v, const std::vector<int32_t>& v,
Assimp::StreamWriterLE& s Assimp::StreamWriterLE& s
){ ){
Node node(name); FBX::Node node(name);
node.Begin(s); node.BeginBinary(s);
s.PutU1('i'); s.PutU1('i');
s.PutU4(uint32_t(v.size())); // number of elements s.PutU4(uint32_t(v.size())); // number of elements
s.PutU4(0); // no encoding (1 would be zip-compressed) s.PutU4(0); // no encoding (1 would be zip-compressed)
s.PutU4(uint32_t(v.size()) * 4); // data size s.PutU4(uint32_t(v.size()) * 4); // data size
for (auto it = v.begin(); it != v.end(); ++it) { s.PutI4(*it); } for (auto it = v.begin(); it != v.end(); ++it) { s.PutI4(*it); }
node.EndProperties(s, 1); node.EndPropertiesBinary(s, 1);
node.End(s, false); 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_FBX_EXPORTER
#endif // ASSIMP_BUILD_NO_EXPORT #endif // ASSIMP_BUILD_NO_EXPORT

View File

@ -66,14 +66,18 @@ public: // public data members
std::vector<FBX::Property> properties; // node properties std::vector<FBX::Property> properties; // node properties
std::vector<FBX::Node> children; // child nodes std::vector<FBX::Node> children; // child nodes
// some nodes always pretend they have children...
bool force_has_children = false;
public: // constructors public: // constructors
Node() = default; Node() = default;
Node(const std::string& n) : name(n) {} Node(const std::string& n) : name(n) {}
Node(const std::string& n, const FBX::Property &p)
// convenience template to construct with properties directly
template <typename... More>
Node(const std::string& n, const More... more)
: name(n) : name(n)
{ properties.push_back(p); } { AddProperties(more...); }
Node(const std::string& n, const std::vector<FBX::Property> &pv)
: name(n), properties(pv) {}
public: // functions to add properties or children public: // functions to add properties or children
// add a single property to the node // add a single property to the node
@ -138,19 +142,48 @@ public: // support specifically for dealing with Properties70 nodes
public: // member functions for writing data to a file or stream public: // member functions for writing data to a file or stream
// write the full node as binary data to the given file or stream // write the full node to the given file or stream
void Dump(std::shared_ptr<Assimp::IOStream> outfile); void Dump(
void Dump(Assimp::StreamWriterLE &s); 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. // these other functions are for writing data piece by piece.
// they must be used carefully. // they must be used carefully.
// for usage examples see FBXExporter.cpp. // for usage examples see FBXExporter.cpp.
void Begin(Assimp::StreamWriterLE &s); void Begin(Assimp::StreamWriterLE &s, bool binary, int indent);
void DumpProperties(Assimp::StreamWriterLE& s); void DumpProperties(Assimp::StreamWriterLE& s, bool binary, int indent);
void EndProperties(Assimp::StreamWriterLE &s); void EndProperties(Assimp::StreamWriterLE &s, bool binary, int indent);
void EndProperties(Assimp::StreamWriterLE &s, size_t num_properties); void EndProperties(
void DumpChildren(Assimp::StreamWriterLE& s); Assimp::StreamWriterLE &s, bool binary, int indent,
void End(Assimp::StreamWriterLE &s, bool has_children); 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 private: // data used for binary dumps
size_t start_pos; // starting position in stream size_t start_pos; // starting position in stream
@ -165,11 +198,12 @@ public: // static member functions
static void WritePropertyNode( static void WritePropertyNode(
const std::string& name, const std::string& name,
const T value, const T value,
Assimp::StreamWriterLE& s Assimp::StreamWriterLE& s,
bool binary, int indent
) { ) {
FBX::Property p(value); FBX::Property p(value);
FBX::Node node(name, p); FBX::Node node(name, p);
node.Dump(s); node.Dump(s, binary, indent);
} }
// convenience function to create and write a property node, // convenience function to create and write a property node,
@ -178,7 +212,8 @@ public: // static member functions
static void WritePropertyNode( static void WritePropertyNode(
const std::string& name, const std::string& name,
const std::vector<double>& v, const std::vector<double>& v,
Assimp::StreamWriterLE& s Assimp::StreamWriterLE& s,
bool binary, int indent
); );
// convenience function to create and write a property node, // convenience function to create and write a property node,
@ -187,8 +222,34 @@ public: // static member functions
static void WritePropertyNode( static void WritePropertyNode(
const std::string& name, const std::string& name,
const std::vector<int32_t>& v, 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 Assimp::StreamWriterLE& s
); );
static void WritePropertyNodeBinary(
const std::string& name,
const std::vector<int32_t>& v,
Assimp::StreamWriterLE& s
);
}; };

View File

@ -48,7 +48,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <string> #include <string>
#include <vector> #include <vector>
#include <sstream> // stringstream #include <ostream>
#include <locale>
#include <sstream> // ostringstream
// constructors for single element properties // 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); s.PutU1(type);
uint8_t* d; uint8_t* d = data.data();
size_t N; size_t N;
switch (type) { switch (type) {
case 'C': s.PutU1(*(reinterpret_cast<uint8_t*>(data.data()))); return; case 'C': s.PutU1(*(reinterpret_cast<uint8_t*>(d))); return;
case 'Y': s.PutI2(*(reinterpret_cast<int16_t*>(data.data()))); return; case 'Y': s.PutI2(*(reinterpret_cast<int16_t*>(d))); return;
case 'I': s.PutI4(*(reinterpret_cast<int32_t*>(data.data()))); return; case 'I': s.PutI4(*(reinterpret_cast<int32_t*>(d))); return;
case 'F': s.PutF4(*(reinterpret_cast<float*>(data.data()))); return; case 'F': s.PutF4(*(reinterpret_cast<float*>(d))); return;
case 'D': s.PutF8(*(reinterpret_cast<double*>(data.data()))); return; case 'D': s.PutF8(*(reinterpret_cast<double*>(d))); return;
case 'L': s.PutI8(*(reinterpret_cast<int64_t*>(data.data()))); return; case 'L': s.PutI8(*(reinterpret_cast<int64_t*>(d))); return;
case 'S': case 'S':
case 'R': case 'R':
s.PutU4(uint32_t(data.size())); 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) s.PutU4(0); // no encoding (1 would be zip-compressed)
// TODO: compress if large? // TODO: compress if large?
s.PutU4(uint32_t(data.size())); // data size s.PutU4(uint32_t(data.size())); // data size
d = data.data();
for (size_t i = 0; i < N; ++i) { for (size_t i = 0; i < N; ++i) {
s.PutI4((reinterpret_cast<int32_t*>(d))[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) s.PutU4(0); // no encoding (1 would be zip-compressed)
// TODO: compress if large? // TODO: compress if large?
s.PutU4(uint32_t(data.size())); // data size s.PutU4(uint32_t(data.size())); // data size
d = data.data();
for (size_t i = 0; i < N; ++i) { for (size_t i = 0; i < N; ++i) {
s.PutI8((reinterpret_cast<int64_t*>(d))[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) s.PutU4(0); // no encoding (1 would be zip-compressed)
// TODO: compress if large? // TODO: compress if large?
s.PutU4(uint32_t(data.size())); // data size s.PutU4(uint32_t(data.size())); // data size
d = data.data();
for (size_t i = 0; i < N; ++i) { for (size_t i = 0; i < N; ++i) {
s.PutF4((reinterpret_cast<float*>(d))[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) s.PutU4(0); // no encoding (1 would be zip-compressed)
// TODO: compress if large? // TODO: compress if large?
s.PutU4(uint32_t(data.size())); // data size s.PutU4(uint32_t(data.size())); // data size
d = data.data();
for (size_t i = 0; i < N; ++i) { for (size_t i = 0; i < N; ++i) {
s.PutF8((reinterpret_cast<double*>(d))[i]); s.PutF8((reinterpret_cast<double*>(d))[i]);
} }
return; return;
default: default:
std::stringstream err; std::ostringstream err;
err << "Tried to dump property with invalid type '"; err << "Tried to dump property with invalid type '";
err << type << "'!"; err << type << "'!";
throw DeadlyExportError(err.str()); 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_FBX_EXPORTER
#endif // ASSIMP_BUILD_NO_EXPORT #endif // ASSIMP_BUILD_NO_EXPORT

View File

@ -53,6 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <string> #include <string>
#include <vector> #include <vector>
#include <ostream>
#include <type_traits> // is_void #include <type_traits> // is_void
namespace FBX { namespace FBX {
@ -113,7 +114,10 @@ public:
size_t size(); size_t size();
// write this property node as binary data to the given stream // 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: private:
char type; char type;

View File

@ -66,7 +66,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <vector> #include <vector>
#include <array> #include <array>
#include <unordered_set> #include <unordered_set>
#include <iostream> // endl
// RESOURCES: // RESOURCES:
// https://code.blender.org/2013/08/fbx-binary-file-format-specification/ // 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"; "\xfa\xbc\xab\x09\xd0\xc8\xd4\x66\xb1\x76\xfb\x83\x1c\xf7\x26\x7e";
const std::string FOOT_MAGIC = const std::string FOOT_MAGIC =
"\xf8\x5a\x8c\x6a\xde\xf5\xd9\x7e\xec\xe9\x0c\xe3\x75\x8f\x29\x0b"; "\xf8\x5a\x8c\x6a\xde\xf5\xd9\x7e\xec\xe9\x0c\xe3\x75\x8f\x29\x0b";
const std::string COMMENT_UNDERLINE =
";------------------------------------------------------------------";
} }
using namespace Assimp; using namespace Assimp;
@ -115,7 +116,7 @@ namespace Assimp {
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
// Worker function for exporting a scene to ASCII FBX. // Worker function for exporting a scene to ASCII FBX.
// Prototyped and registered in Exporter.cpp // Prototyped and registered in Exporter.cpp
/*void ExportSceneFBXA ( void ExportSceneFBXA (
const char* pFile, const char* pFile,
IOSystem* pIOSystem, IOSystem* pIOSystem,
const aiScene* pScene, const aiScene* pScene,
@ -126,7 +127,7 @@ namespace Assimp {
// perform ascii export // perform ascii export
exporter.ExportAscii(pFile, pIOSystem); exporter.ExportAscii(pFile, pIOSystem);
}*/ // TODO }
} // end of namespace Assimp } // end of namespace Assimp
@ -194,27 +195,43 @@ void FBXExporter::ExportAscii (
); );
} }
// this isn't really necessary, // write the ascii header
// but the Autodesk FBX SDK puts a similar comment at the top of the file. WriteAsciiHeader();
// 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 all the sections // write all the sections
WriteAllNodes(); 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, // explicitly release file pointer,
// so we don't have to rely on class destruction. // so we don't have to rely on class destruction.
outfile.reset(); 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() void FBXExporter::WriteBinaryHeader()
{ {
// first a specific sequence of 23 bytes, always the same // first a specific sequence of 23 bytes, always the same
@ -294,28 +311,39 @@ void FBXExporter::WriteAllNodes ()
//FBXHeaderExtension top-level node //FBXHeaderExtension top-level node
void FBXExporter::WriteHeaderExtension () void FBXExporter::WriteHeaderExtension ()
{ {
if (!binary) {
// no title, follows directly from the top comment
}
FBX::Node n("FBXHeaderExtension"); FBX::Node n("FBXHeaderExtension");
StreamWriterLE outstream(outfile); StreamWriterLE outstream(outfile);
int indent = 0;
// begin node // begin node
n.Begin(outstream); n.Begin(outstream, binary, indent);
// write properties // write properties
// (none) // (none)
// finish properties // finish properties
n.EndProperties(outstream, 0); n.EndProperties(outstream, binary, indent, 0);
// begin children
n.BeginChildren(outstream, binary, indent);
indent = 1;
// write child nodes // write child nodes
FBX::Node::WritePropertyNode( FBX::Node::WritePropertyNode(
"FBXHeaderVersion", int32_t(1003), outstream "FBXHeaderVersion", int32_t(1003), outstream, binary, indent
); );
FBX::Node::WritePropertyNode( FBX::Node::WritePropertyNode(
"FBXVersion", int32_t(EXPORT_VERSION_INT), outstream "FBXVersion", int32_t(EXPORT_VERSION_INT), outstream, binary, indent
);
FBX::Node::WritePropertyNode(
"EncryptionType", int32_t(0), outstream
); );
if (binary) {
FBX::Node::WritePropertyNode(
"EncryptionType", int32_t(0), outstream, binary, indent
);
}
FBX::Node CreationTimeStamp("CreationTimeStamp"); FBX::Node CreationTimeStamp("CreationTimeStamp");
time_t rawtime; time_t rawtime;
@ -329,36 +357,50 @@ void FBXExporter::WriteHeaderExtension ()
CreationTimeStamp.AddChild("Minute", int32_t(now->tm_min)); CreationTimeStamp.AddChild("Minute", int32_t(now->tm_min));
CreationTimeStamp.AddChild("Second", int32_t(now->tm_sec)); CreationTimeStamp.AddChild("Second", int32_t(now->tm_sec));
CreationTimeStamp.AddChild("Millisecond", int32_t(0)); CreationTimeStamp.AddChild("Millisecond", int32_t(0));
CreationTimeStamp.Dump(outstream); CreationTimeStamp.Dump(outstream, binary, indent);
std::stringstream creator; std::stringstream creator;
creator << "Open Asset Import Library (Assimp) " << aiGetVersionMajor() creator << "Open Asset Import Library (Assimp) " << aiGetVersionMajor()
<< "." << aiGetVersionMinor() << "." << aiGetVersionRevision(); << "." << 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"); //sceneinfo.AddProperty("GlobalInfo" + FBX::SEPARATOR + "SceneInfo");
// not sure if any of this is actually needed, // not sure if any of this is actually needed,
// so just write an empty node for now. // so just write an empty node for now.
sceneinfo.Dump(outstream); //sceneinfo.Dump(outstream, binary, indent);
indent = 0;
// finish node // finish node
n.End(outstream, true); n.End(outstream, binary, indent, true);
// that's it for FBXHeaderExtension... // that's it for FBXHeaderExtension...
if (!binary) { return; }
// but binary files also need top-level FileID, CreationTime, Creator: // but binary files also need top-level FileID, CreationTime, Creator:
std::vector<uint8_t> raw(GENERIC_FILEID.size()); std::vector<uint8_t> raw(GENERIC_FILEID.size());
for (size_t i = 0; i < GENERIC_FILEID.size(); ++i) { for (size_t i = 0; i < GENERIC_FILEID.size(); ++i) {
raw[i] = uint8_t(GENERIC_FILEID[i]); raw[i] = uint8_t(GENERIC_FILEID[i]);
} }
FBX::Node::WritePropertyNode("FileId", raw, outstream); FBX::Node::WritePropertyNode(
FBX::Node::WritePropertyNode("CreationTime", GENERIC_CTIME, outstream); "FileId", raw, outstream, binary, indent
FBX::Node::WritePropertyNode("Creator", creator.str(), outstream); );
FBX::Node::WritePropertyNode(
"CreationTime", GENERIC_CTIME, outstream, binary, indent
);
FBX::Node::WritePropertyNode(
"Creator", creator.str(), outstream, binary, indent
);
} }
void FBXExporter::WriteGlobalSettings () void FBXExporter::WriteGlobalSettings ()
{ {
if (!binary) {
// no title, follows directly from the header extension
}
FBX::Node gs("GlobalSettings"); FBX::Node gs("GlobalSettings");
gs.AddChild("Version", int32_t(1000)); gs.AddChild("Version", int32_t(1000));
@ -385,11 +427,15 @@ void FBXExporter::WriteGlobalSettings ()
p.AddP70int("CurrentTimeMarker", -1); p.AddP70int("CurrentTimeMarker", -1);
gs.AddChild(p); gs.AddChild(p);
gs.Dump(outfile); gs.Dump(outfile, binary, 0);
} }
void FBXExporter::WriteDocuments () void FBXExporter::WriteDocuments ()
{ {
if (!binary) {
WriteAsciiSectionHeader("Documents Description");
}
// not sure what the use of multiple documents would be, // not sure what the use of multiple documents would be,
// or whether any end-application supports it // or whether any end-application supports it
FBX::Node docs("Documents"); FBX::Node docs("Documents");
@ -411,15 +457,19 @@ void FBXExporter::WriteDocuments ()
doc.AddChild("RootNode", int64_t(0)); doc.AddChild("RootNode", int64_t(0));
docs.AddChild(doc); docs.AddChild(doc);
docs.Dump(outfile); docs.Dump(outfile, binary, 0);
} }
void FBXExporter::WriteReferences () void FBXExporter::WriteReferences ()
{ {
if (!binary) {
WriteAsciiSectionHeader("Document References");
}
// always empty for now. // always empty for now.
// not really sure what this is for. // not really sure what this is for.
FBX::Node n("References"); FBX::Node n("References");
n.Dump(outfile); n.force_has_children = true;
n.Dump(outfile, binary, 0);
} }
@ -468,9 +518,6 @@ size_t count_images(const aiScene* scene) {
} }
} }
} }
//for (auto &s : images) {
// std::cout << "found image: " << s << std::endl;
//}
return images.size(); return images.size();
} }
@ -510,6 +557,11 @@ void FBXExporter::WriteDefinitions ()
// determining how many of each type of object there are // determining how many of each type of object there are
// and specifying the base properties to use when otherwise unspecified. // and specifying the base properties to use when otherwise unspecified.
// ascii section header
if (!binary) {
WriteAsciiSectionHeader("Object definitions");
}
// we need to count the objects // we need to count the objects
int32_t count; int32_t count;
int32_t total_count = 0; int32_t total_count = 0;
@ -520,7 +572,7 @@ void FBXExporter::WriteDefinitions ()
// GlobalSettings // GlobalSettings
// this seems to always be here in Maya exports // this seems to always be here in Maya exports
n = FBX::Node("ObjectType", Property("GlobalSettings")); n = FBX::Node("ObjectType", "GlobalSettings");
count = 1; count = 1;
n.AddChild("Count", count); n.AddChild("Count", count);
object_nodes.push_back(n); object_nodes.push_back(n);
@ -531,9 +583,9 @@ void FBXExporter::WriteDefinitions ()
// but no harm seems to come of leaving it out. // but no harm seems to come of leaving it out.
count = mScene->mNumAnimations; count = mScene->mNumAnimations;
if (count) { if (count) {
n = FBX::Node("ObjectType", Property("AnimationStack")); n = FBX::Node("ObjectType", "AnimationStack");
n.AddChild("Count", count); n.AddChild("Count", count);
pt = FBX::Node("PropertyTemplate", Property("FbxAnimStack")); pt = FBX::Node("PropertyTemplate", "FbxAnimStack");
p = FBX::Node("Properties70"); p = FBX::Node("Properties70");
p.AddP70string("Description", ""); p.AddP70string("Description", "");
p.AddP70time("LocalStart", 0); p.AddP70time("LocalStart", 0);
@ -553,9 +605,9 @@ void FBXExporter::WriteDefinitions ()
// so there will be one per aiAnimation // so there will be one per aiAnimation
count = mScene->mNumAnimations; count = mScene->mNumAnimations;
if (count) { if (count) {
n = FBX::Node("ObjectType", Property("AnimationLayer")); n = FBX::Node("ObjectType", "AnimationLayer");
n.AddChild("Count", count); n.AddChild("Count", count);
pt = FBX::Node("PropertyTemplate", Property("FBXAnimLayer")); pt = FBX::Node("PropertyTemplate", "FBXAnimLayer");
p = FBX::Node("Properties70"); p = FBX::Node("Properties70");
p.AddP70("Weight", "Number", "", "A", double(100)); p.AddP70("Weight", "Number", "", "A", double(100));
p.AddP70bool("Mute", 0); p.AddP70bool("Mute", 0);
@ -583,9 +635,9 @@ void FBXExporter::WriteDefinitions ()
count = 1; // TODO: select properly count = 1; // TODO: select properly
if (count) { if (count) {
// FbxSkeleton // FbxSkeleton
n = FBX::Node("ObjectType", Property("NodeAttribute")); n = FBX::Node("ObjectType", "NodeAttribute");
n.AddChild("Count", count); n.AddChild("Count", count);
pt = FBX::Node("PropertyTemplate", Property("FbxSkeleton")); pt = FBX::Node("PropertyTemplate", "FbxSkeleton");
p = FBX::Node("Properties70"); p = FBX::Node("Properties70");
p.AddP70color("Color", 0.8, 0.8, 0.8); p.AddP70color("Color", 0.8, 0.8, 0.8);
p.AddP70double("Size", 33.333333333333); p.AddP70double("Size", 33.333333333333);
@ -601,9 +653,9 @@ void FBXExporter::WriteDefinitions ()
// <~~ node heirarchy // <~~ node heirarchy
count = int32_t(count_nodes(mScene->mRootNode)) - 1; // (not counting root node) count = int32_t(count_nodes(mScene->mRootNode)) - 1; // (not counting root node)
if (count) { if (count) {
n = FBX::Node("ObjectType", Property("Model")); n = FBX::Node("ObjectType", "Model");
n.AddChild("Count", count); n.AddChild("Count", count);
pt = FBX::Node("PropertyTemplate", Property("FbxNode")); pt = FBX::Node("PropertyTemplate", "FbxNode");
p = FBX::Node("Properties70"); p = FBX::Node("Properties70");
p.AddP70enum("QuaternionInterpolate", 0); p.AddP70enum("QuaternionInterpolate", 0);
p.AddP70vector("RotationOffset", 0.0, 0.0, 0.0); p.AddP70vector("RotationOffset", 0.0, 0.0, 0.0);
@ -698,9 +750,9 @@ void FBXExporter::WriteDefinitions ()
// <~~ aiMesh // <~~ aiMesh
count = mScene->mNumMeshes; count = mScene->mNumMeshes;
if (count) { if (count) {
n = FBX::Node("ObjectType", Property("Geometry")); n = FBX::Node("ObjectType", "Geometry");
n.AddChild("Count", count); n.AddChild("Count", count);
pt = FBX::Node("PropertyTemplate", Property("FbxMesh")); pt = FBX::Node("PropertyTemplate", "FbxMesh");
p = FBX::Node("Properties70"); p = FBX::Node("Properties70");
p.AddP70color("Color", 0, 0, 0); p.AddP70color("Color", 0, 0, 0);
p.AddP70vector("BBoxMin", 0, 0, 0); p.AddP70vector("BBoxMin", 0, 0, 0);
@ -724,7 +776,7 @@ void FBXExporter::WriteDefinitions ()
count = mScene->mNumMaterials; count = mScene->mNumMaterials;
if (count) { if (count) {
bool has_phong = has_phong_mat(mScene); bool has_phong = has_phong_mat(mScene);
n = FBX::Node("ObjectType", Property("Material")); n = FBX::Node("ObjectType", "Material");
n.AddChild("Count", count); n.AddChild("Count", count);
pt = FBX::Node("PropertyTemplate"); pt = FBX::Node("PropertyTemplate");
if (has_phong) { if (has_phong) {
@ -771,9 +823,9 @@ void FBXExporter::WriteDefinitions ()
// one for each image file. // one for each image file.
count = int32_t(count_images(mScene)); count = int32_t(count_images(mScene));
if (count) { if (count) {
n = FBX::Node("ObjectType", Property("Video")); n = FBX::Node("ObjectType", "Video");
n.AddChild("Count", count); n.AddChild("Count", count);
pt = FBX::Node("PropertyTemplate", Property("FbxVideo")); pt = FBX::Node("PropertyTemplate", "FbxVideo");
p = FBX::Node("Properties70"); p = FBX::Node("Properties70");
p.AddP70bool("ImageSequence", 0); p.AddP70bool("ImageSequence", 0);
p.AddP70int("ImageSequenceOffset", 0); p.AddP70int("ImageSequenceOffset", 0);
@ -800,9 +852,9 @@ void FBXExporter::WriteDefinitions ()
// <~~ aiTexture // <~~ aiTexture
count = int32_t(count_textures(mScene)); count = int32_t(count_textures(mScene));
if (count) { if (count) {
n = FBX::Node("ObjectType", Property("Texture")); n = FBX::Node("ObjectType", "Texture");
n.AddChild("Count", count); n.AddChild("Count", count);
pt = FBX::Node("PropertyTemplate", Property("FbxFileTexture")); pt = FBX::Node("PropertyTemplate", "FbxFileTexture");
p = FBX::Node("Properties70"); p = FBX::Node("Properties70");
p.AddP70enum("TextureTypeUse", 0); p.AddP70enum("TextureTypeUse", 0);
p.AddP70numberA("Texture alpha", 1.0); p.AddP70numberA("Texture alpha", 1.0);
@ -829,9 +881,9 @@ void FBXExporter::WriteDefinitions ()
// AnimationCurveNode / FbxAnimCurveNode // AnimationCurveNode / FbxAnimCurveNode
count = mScene->mNumAnimations * 3; count = mScene->mNumAnimations * 3;
if (count) { if (count) {
n = FBX::Node("ObjectType", Property("AnimationCurveNode")); n = FBX::Node("ObjectType", "AnimationCurveNode");
n.AddChild("Count", count); n.AddChild("Count", count);
pt = FBX::Node("PropertyTemplate", Property("FbxAnimCurveNode")); pt = FBX::Node("PropertyTemplate", "FbxAnimCurveNode");
p = FBX::Node("Properties70"); p = FBX::Node("Properties70");
p.AddP70("d", "Compound", "", ""); p.AddP70("d", "Compound", "", "");
pt.AddChild(p); pt.AddChild(p);
@ -843,7 +895,7 @@ void FBXExporter::WriteDefinitions ()
// AnimationCurve / FbxAnimCurve // AnimationCurve / FbxAnimCurve
count = mScene->mNumAnimations * 9; count = mScene->mNumAnimations * 9;
if (count) { if (count) {
n = FBX::Node("ObjectType", Property("AnimationCurve")); n = FBX::Node("ObjectType", "AnimationCurve");
n.AddChild("Count", count); n.AddChild("Count", count);
object_nodes.push_back(n); object_nodes.push_back(n);
total_count += count; total_count += count;
@ -856,7 +908,7 @@ void FBXExporter::WriteDefinitions ()
if (mesh->HasBones()) { ++count; } if (mesh->HasBones()) { ++count; }
} }
if (count) { if (count) {
n = FBX::Node("ObjectType", Property("Pose")); n = FBX::Node("ObjectType", "Pose");
n.AddChild("Count", count); n.AddChild("Count", count);
object_nodes.push_back(n); object_nodes.push_back(n);
total_count += count; total_count += count;
@ -865,7 +917,7 @@ void FBXExporter::WriteDefinitions ()
// Deformer // Deformer
count = int32_t(count_deformers(mScene)); count = int32_t(count_deformers(mScene));
if (count) { if (count) {
n = FBX::Node("ObjectType", Property("Deformer")); n = FBX::Node("ObjectType", "Deformer");
n.AddChild("Count", count); n.AddChild("Count", count);
object_nodes.push_back(n); object_nodes.push_back(n);
total_count += count; total_count += count;
@ -874,9 +926,9 @@ void FBXExporter::WriteDefinitions ()
// (template) // (template)
count = 0; count = 0;
if (count) { if (count) {
n = FBX::Node("ObjectType", Property("")); n = FBX::Node("ObjectType", "");
n.AddChild("Count", count); n.AddChild("Count", count);
pt = FBX::Node("PropertyTemplate", Property("")); pt = FBX::Node("PropertyTemplate", "");
p = FBX::Node("Properties70"); p = FBX::Node("Properties70");
pt.AddChild(p); pt.AddChild(p);
n.AddChild(pt); n.AddChild(pt);
@ -889,7 +941,7 @@ void FBXExporter::WriteDefinitions ()
defs.AddChild("Version", int32_t(100)); defs.AddChild("Version", int32_t(100));
defs.AddChild("Count", int32_t(total_count)); defs.AddChild("Count", int32_t(total_count));
for (auto &n : object_nodes) { defs.AddChild(n); } for (auto &n : object_nodes) { defs.AddChild(n); }
defs.Dump(outfile); defs.Dump(outfile, binary, 0);
} }
@ -935,14 +987,20 @@ int64_t to_ktime(double ticks, const aiAnimation* anim) {
void FBXExporter::WriteObjects () void FBXExporter::WriteObjects ()
{ {
if (!binary) {
WriteAsciiSectionHeader("Object properties");
}
// numbers should match those given in definitions! make sure to check // numbers should match those given in definitions! make sure to check
StreamWriterLE outstream(outfile); StreamWriterLE outstream(outfile);
FBX::Node object_node("Objects"); FBX::Node object_node("Objects");
object_node.Begin(outstream); int indent = 0;
object_node.EndProperties(outstream); object_node.Begin(outstream, binary, indent);
object_node.EndProperties(outstream, binary, indent);
object_node.BeginChildren(outstream, binary, indent);
// geometry (aiMesh) // geometry (aiMesh)
mesh_uids.clear(); mesh_uids.clear();
indent = 1;
for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) { for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) {
// it's all about this mesh // it's all about this mesh
aiMesh* m = mScene->mMeshes[mi]; aiMesh* m = mScene->mMeshes[mi];
@ -954,9 +1012,11 @@ void FBXExporter::WriteObjects ()
n.AddProperty(uid); n.AddProperty(uid);
n.AddProperty(FBX::SEPARATOR + "Geometry"); n.AddProperty(FBX::SEPARATOR + "Geometry");
n.AddProperty("Mesh"); n.AddProperty("Mesh");
n.Begin(outstream); n.Begin(outstream, binary, indent);
n.DumpProperties(outstream); n.DumpProperties(outstream, binary, indent);
n.EndProperties(outstream); n.EndProperties(outstream, binary, indent);
n.BeginChildren(outstream, binary, indent);
indent = 2;
// output vertex data - each vertex should be unique (probably) // output vertex data - each vertex should be unique (probably)
std::vector<double> flattened_vertices; std::vector<double> flattened_vertices;
@ -980,7 +1040,7 @@ void FBXExporter::WriteObjects ()
} }
} }
FBX::Node::WritePropertyNode( FBX::Node::WritePropertyNode(
"Vertices", flattened_vertices, outstream "Vertices", flattened_vertices, outstream, binary, indent
); );
// output polygon data as a flattened array of vertex indices. // output polygon data as a flattened array of vertex indices.
@ -996,30 +1056,38 @@ void FBXExporter::WriteObjects ()
); );
} }
FBX::Node::WritePropertyNode( FBX::Node::WritePropertyNode(
"PolygonVertexIndex", polygon_data, outstream "PolygonVertexIndex", polygon_data, outstream, binary, indent
); );
// here could be edges but they're insane. // here could be edges but they're insane.
// it's optional anyway, so let's ignore it. // it's optional anyway, so let's ignore it.
FBX::Node::WritePropertyNode( FBX::Node::WritePropertyNode(
"GeometryVersion", int32_t(124), outstream "GeometryVersion", int32_t(124), outstream, binary, indent
); );
// normals, if any // normals, if any
if (m->HasNormals()) { if (m->HasNormals()) {
FBX::Node normals("LayerElementNormal", Property(int32_t(0))); FBX::Node normals("LayerElementNormal", int32_t(0));
normals.Begin(outstream); normals.Begin(outstream, binary, indent);
normals.DumpProperties(outstream); normals.DumpProperties(outstream, binary, indent);
normals.EndProperties(outstream); normals.EndProperties(outstream, binary, indent);
FBX::Node::WritePropertyNode("Version", int32_t(101), outstream); normals.BeginChildren(outstream, binary, indent);
FBX::Node::WritePropertyNode("Name", "", outstream); indent = 3;
FBX::Node::WritePropertyNode( 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 // TODO: vertex-normals or indexed normals when appropriate
FBX::Node::WritePropertyNode( FBX::Node::WritePropertyNode(
"ReferenceInformationType", "Direct", outstream "ReferenceInformationType", "Direct",
outstream, binary, indent
); );
std::vector<double> normal_data; std::vector<double> normal_data;
normal_data.reserve(3 * polygon_data.size()); normal_data.reserve(3 * polygon_data.size());
@ -1032,10 +1100,13 @@ void FBXExporter::WriteObjects ()
normal_data.push_back(n.z); 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, // note: version 102 has a NormalsW also... not sure what it is,
// so we can stick with version 101 for now. // so we can stick with version 101 for now.
normals.End(outstream, true); indent = 2;
normals.End(outstream, binary, indent, true);
} }
// uvs, if any // uvs, if any
@ -1055,19 +1126,27 @@ void FBXExporter::WriteObjects ()
err << " but may be incorrectly interpreted on load."; err << " but may be incorrectly interpreted on load.";
DefaultLogger::get()->warn(err.str()); DefaultLogger::get()->warn(err.str());
} }
FBX::Node uv("LayerElementUV", Property(int32_t(uvi))); FBX::Node uv("LayerElementUV", int32_t(uvi));
uv.Begin(outstream); uv.Begin(outstream, binary, indent);
uv.DumpProperties(outstream); uv.DumpProperties(outstream, binary, indent);
uv.EndProperties(outstream); uv.EndProperties(outstream, binary, indent);
FBX::Node::WritePropertyNode("Version", int32_t(101), outstream); 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, // it doesn't seem like assimp keeps the uv map name,
// so just leave it blank. // so just leave it blank.
FBX::Node::WritePropertyNode("Name", "", outstream);
FBX::Node::WritePropertyNode( FBX::Node::WritePropertyNode(
"MappingInformationType", "ByPolygonVertex", outstream "Name", "", outstream, binary, indent
); );
FBX::Node::WritePropertyNode( FBX::Node::WritePropertyNode(
"ReferenceInformationType", "IndexToDirect", outstream "MappingInformationType", "ByPolygonVertex",
outstream, binary, indent
);
FBX::Node::WritePropertyNode(
"ReferenceInformationType", "IndexToDirect",
outstream, binary, indent
); );
std::vector<double> uv_data; std::vector<double> uv_data;
@ -1092,27 +1171,32 @@ void FBXExporter::WriteObjects ()
} }
} }
} }
FBX::Node::WritePropertyNode("UV", uv_data, outstream); FBX::Node::WritePropertyNode(
FBX::Node::WritePropertyNode("UVIndex", uv_indices, outstream); "UV", uv_data, outstream, binary, indent
uv.End(outstream, true); );
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, // i'm not really sure why this material section exists,
// as the material is linked via "Connections". // as the material is linked via "Connections".
// it seems to always have the same "0" value. // it seems to always have the same "0" value.
FBX::Node mat("LayerElementMaterial", Property(int32_t(0))); FBX::Node mat("LayerElementMaterial", int32_t(0));
mat.AddChild("Version", int32_t(101)); mat.AddChild("Version", int32_t(101));
mat.AddChild("Name", ""); mat.AddChild("Name", "");
mat.AddChild("MappingInformationType", "AllSame"); mat.AddChild("MappingInformationType", "AllSame");
mat.AddChild("ReferenceInformationType", "IndexToDirect"); mat.AddChild("ReferenceInformationType", "IndexToDirect");
std::vector<int32_t> mat_indices = {0}; std::vector<int32_t> mat_indices = {0};
mat.AddChild("Materials", mat_indices); mat.AddChild("Materials", mat_indices);
mat.Dump(outstream); mat.Dump(outstream, binary, indent);
// finally we have the layer specifications, // finally we have the layer specifications,
// which select the normals / UV set / etc to use. // which select the normals / UV set / etc to use.
// TODO: handle multiple uv sets correctly? // TODO: handle multiple uv sets correctly?
FBX::Node layer("Layer", Property(int32_t(0))); FBX::Node layer("Layer", int32_t(0));
layer.AddChild("Version", int32_t(100)); layer.AddChild("Version", int32_t(100));
FBX::Node le("LayerElement"); FBX::Node le("LayerElement");
le.AddChild("Type", "LayerElementNormal"); le.AddChild("Type", "LayerElementNormal");
@ -1126,10 +1210,11 @@ void FBXExporter::WriteObjects ()
le.AddChild("Type", "LayerElementUV"); le.AddChild("Type", "LayerElementUV");
le.AddChild("TypedIndex", int32_t(0)); le.AddChild("TypedIndex", int32_t(0));
layer.AddChild(le); layer.AddChild(le);
layer.Dump(outstream); layer.Dump(outstream, binary, indent);
// finish the node record // finish the node record
n.End(outstream, true); indent = 1;
n.End(outstream, binary, indent, true);
} }
// aiMaterial // aiMaterial
@ -1273,7 +1358,7 @@ void FBXExporter::WriteObjects ()
n.AddChild(p); n.AddChild(p);
n.Dump(outstream); n.Dump(outstream, binary, indent);
} }
// we need to look up all the images we're using, // we need to look up all the images we're using,
@ -1321,7 +1406,7 @@ void FBXExporter::WriteObjects ()
n.AddChild("UseMipMap", int32_t(0)); n.AddChild("UseMipMap", int32_t(0));
n.AddChild("Filename", path); n.AddChild("Filename", path);
n.AddChild("RelativeFilename", path); n.AddChild("RelativeFilename", path);
n.Dump(outstream); n.Dump(outstream, binary, indent);
} }
// Textures // Textures
@ -1408,14 +1493,12 @@ void FBXExporter::WriteObjects ()
const int64_t texture_uid = generate_uid(); const int64_t texture_uid = generate_uid();
// link the texture to the material // link the texture to the material
FBX::Node c("C"); connections.emplace_back(
c.AddProperties("OP", texture_uid, material_uid, prop_name); "C", "OP", texture_uid, material_uid, prop_name
connections.push_back(c); );
// link the image data to the texture // link the image data to the texture
c = FBX::Node("C"); connections.emplace_back("C", "OO", image_uid, texture_uid);
c.AddProperties("OO", image_uid, texture_uid);
connections.push_back(c);
// now write the actual texture node // now write the actual texture node
FBX::Node tnode("Texture"); FBX::Node tnode("Texture");
@ -1438,11 +1521,11 @@ void FBXExporter::WriteObjects ()
tnode.AddChild("RelativeFilename", texture_path); tnode.AddChild("RelativeFilename", texture_path);
tnode.AddChild("ModelUVTranslation", double(0.0), double(0.0)); tnode.AddChild("ModelUVTranslation", double(0.0), double(0.0));
tnode.AddChild("ModelUVScaling", double(1.0), double(1.0)); tnode.AddChild("ModelUVScaling", double(1.0), double(1.0));
tnode.AddChild("Texture_Alpha_Soutce", "None"); tnode.AddChild("Texture_Alpha_Source", "None");
tnode.AddChild( tnode.AddChild(
"Cropping", int32_t(0), int32_t(0), int32_t(0), int32_t(0) "Cropping", int32_t(0), int32_t(0), int32_t(0), int32_t(0)
); );
tnode.Dump(outstream); tnode.Dump(outstream, binary, indent);
} }
} }
@ -1595,12 +1678,10 @@ void FBXExporter::WriteObjects ()
// "acuracy"... this is not a typo.... // "acuracy"... this is not a typo....
dnode.AddChild("Link_DeformAcuracy", double(50)); dnode.AddChild("Link_DeformAcuracy", double(50));
dnode.AddChild("SkinningType", "Linear"); // TODO: other modes? dnode.AddChild("SkinningType", "Linear"); // TODO: other modes?
dnode.Dump(outstream); dnode.Dump(outstream, binary, indent);
// connect it // connect it
FBX::Node c("C"); connections.emplace_back("C", "OO", deformer_uid, mesh_uids[mi]);
c.AddProperties("OO", deformer_uid, mesh_uids[mi]);
connections.push_back(c); // TODO: emplace_back
// we will be indexing by vertex... // we will be indexing by vertex...
// but there might be a different number of "vertices" // but there might be a different number of "vertices"
@ -1703,7 +1784,7 @@ void FBXExporter::WriteObjects ()
// this should be the same as the bone's mOffsetMatrix. // this should be the same as the bone's mOffsetMatrix.
// if it's not the same, the skeleton isn't in the bind pose. // if it's not the same, the skeleton isn't in the bind pose.
const float epsilon = 1e-5f; // some error is to be expected const float epsilon = 1e-4f; // some error is to be expected
bool bone_xform_okay = true; bool bone_xform_okay = true;
if (b && ! tr.Equal(b->mOffsetMatrix, epsilon)) { if (b && ! tr.Equal(b->mOffsetMatrix, epsilon)) {
not_in_bind_pose.insert(b); not_in_bind_pose.insert(b);
@ -1741,17 +1822,17 @@ void FBXExporter::WriteObjects ()
// there's not really any way around this at the moment. // there's not really any way around this at the moment.
// done // done
sdnode.Dump(outstream); sdnode.Dump(outstream, binary, indent);
// lastly, connect to the parent deformer // lastly, connect to the parent deformer
c = FBX::Node("C"); connections.emplace_back(
c.AddProperties("OO", subdeformer_uid, deformer_uid); "C", "OO", subdeformer_uid, deformer_uid
connections.push_back(c); // TODO: emplace_back );
// we also need to connect the limb node to the subdeformer. // we also need to connect the limb node to the subdeformer.
c = FBX::Node("C"); connections.emplace_back(
c.AddProperties("OO", node_uids[bone_node], subdeformer_uid); "C", "OO", node_uids[bone_node], subdeformer_uid
connections.push_back(c); // TODO: emplace_back );
} }
// if we cannot create a valid FBX file, simply die. // if we cannot create a valid FBX file, simply die.
@ -1859,7 +1940,7 @@ void FBXExporter::WriteObjects ()
} }
// now write it // now write it
bpnode.Dump(outstream); bpnode.Dump(outstream, binary, indent);
}*/ }*/
// TODO: cameras, lights // TODO: cameras, lights
@ -1918,11 +1999,8 @@ void FBXExporter::WriteObjects ()
// this node absurdly always pretends it has children // this node absurdly always pretends it has children
// (in this case it does, but just in case...) // (in this case it does, but just in case...)
asnode.Begin(outstream); asnode.force_has_children = true;
asnode.DumpProperties(outstream); asnode.Dump(outstream, binary, indent);
asnode.EndProperties(outstream);
asnode.DumpChildren(outstream);
asnode.End(outstream, true);
// note: animation stacks are not connected to anything // note: animation stacks are not connected to anything
} }
@ -1936,16 +2014,13 @@ void FBXExporter::WriteObjects ()
alnode.AddProperties(animlayer_uid, FBX::SEPARATOR + "AnimLayer", ""); alnode.AddProperties(animlayer_uid, FBX::SEPARATOR + "AnimLayer", "");
// this node absurdly always pretends it has children // this node absurdly always pretends it has children
alnode.Begin(outstream); alnode.force_has_children = true;
alnode.DumpProperties(outstream); alnode.Dump(outstream, binary, indent);
alnode.EndProperties(outstream);
alnode.DumpChildren(outstream);
alnode.End(outstream, true);
// connect to the relevant animstack // connect to the relevant animstack
FBX::Node c("C"); connections.emplace_back(
c.AddProperties("OO", animlayer_uid, animation_stack_uids[ai]); "C", "OO", animlayer_uid, animation_stack_uids[ai]
connections.push_back(c); // TODO: emplace_back );
} }
// AnimCurveNode - three per aiNodeAnim // AnimCurveNode - three per aiNodeAnim
@ -2057,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, // convenience map of magic node name strings to FBX properties,
@ -2083,13 +2159,14 @@ const std::map<std::string,std::pair<std::string,char>> transform_types = {
}; };
// write a single model node to the stream // write a single model node to the stream
void WriteModelNode( void FBXExporter::WriteModelNode(
StreamWriterLE& outstream, StreamWriterLE& outstream,
bool binary,
const aiNode* node, const aiNode* node,
int64_t node_uid, int64_t node_uid,
const std::string& type, const std::string& type,
const std::vector<std::pair<std::string,aiVector3D>>& transform_chain, 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 zero = {0, 0, 0};
const aiVector3D one = {1, 1, 1}; const aiVector3D one = {1, 1, 1};
@ -2157,7 +2234,7 @@ void WriteModelNode(
m.AddChild("Shading", Property(true)); m.AddChild("Shading", Property(true));
m.AddChild("Culling", Property("CullingOff")); m.AddChild("Culling", Property("CullingOff"));
m.Dump(outstream); m.Dump(outstream, binary, 1);
} }
// wrapper for WriteModelNodes to create and pass a blank transform chain // wrapper for WriteModelNodes to create and pass a blank transform chain
@ -2181,15 +2258,6 @@ void FBXExporter::WriteModelNodes(
// first collapse any expanded transformation chains created by FBX import. // first collapse any expanded transformation chains created by FBX import.
std::string node_name(node->mName.C_Str()); std::string node_name(node->mName.C_Str());
if (node_name.find(MAGIC_NODE_TAG) != std::string::npos) { if (node_name.find(MAGIC_NODE_TAG) != std::string::npos) {
if (node->mNumChildren != 1) {
// this should never happen
std::stringstream err;
err << "FBX transformation node should have exactly 1 child,";
err << " but " << node->mNumChildren << " found";
err << " on node \"" << node_name << "\"!";
throw DeadlyExportError(err.str());
}
aiNode* next_node = node->mChildren[0];
auto pos = node_name.find(MAGIC_NODE_TAG) + MAGIC_NODE_TAG.size() + 1; auto pos = node_name.find(MAGIC_NODE_TAG) + MAGIC_NODE_TAG.size() + 1;
std::string type_name = node_name.substr(pos); std::string type_name = node_name.substr(pos);
auto elem = transform_types.find(type_name); auto elem = transform_types.find(type_name);
@ -2223,10 +2291,16 @@ void FBXExporter::WriteModelNodes(
err << elem->second.second; err << elem->second.second;
throw DeadlyExportError(err.str()); throw DeadlyExportError(err.str());
} }
// now just continue to the next node // now continue on to any child nodes
WriteModelNodes( for (unsigned i = 0; i < node->mNumChildren; ++i) {
outstream, next_node, parent_uid, limbnodes, transform_chain WriteModelNodes(
); outstream,
node->mChildren[i],
parent_uid,
limbnodes,
transform_chain
);
}
return; return;
} }
@ -2240,9 +2314,7 @@ void FBXExporter::WriteModelNodes(
node_uid = generate_uid(); node_uid = generate_uid();
node_uids[node] = node_uid; node_uids[node] = node_uid;
} }
FBX::Node c("C"); connections.emplace_back("C", "OO", node_uid, parent_uid);
c.AddProperties("OO", node_uid, parent_uid);
connections.push_back(c);
} }
// what type of node is this? // what type of node is this?
@ -2250,21 +2322,23 @@ void FBXExporter::WriteModelNodes(
// handled later // handled later
} else if (node->mNumMeshes == 1) { } else if (node->mNumMeshes == 1) {
// connect to child mesh, which should have been written previously // connect to child mesh, which should have been written previously
FBX::Node c("C"); connections.emplace_back(
c.AddProperties("OO", mesh_uids[node->mMeshes[0]], node_uid); "C", "OO", mesh_uids[node->mMeshes[0]], node_uid
connections.push_back(c); );
// also connect to the material for the child mesh // also connect to the material for the child mesh
c = FBX::Node("C"); connections.emplace_back(
c.AddProperties( "C", "OO",
"OO",
material_uids[mScene->mMeshes[node->mMeshes[0]]->mMaterialIndex], material_uids[mScene->mMeshes[node->mMeshes[0]]->mMaterialIndex],
node_uid node_uid
); );
connections.push_back(c);
// write model node // 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)) { } 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 // we also need to write a nodeattribute to mark it as a skeleton
int64_t node_attribute_uid = generate_uid(); int64_t node_attribute_uid = generate_uid();
FBX::Node na("NodeAttribute"); FBX::Node na("NodeAttribute");
@ -2272,14 +2346,14 @@ void FBXExporter::WriteModelNodes(
node_attribute_uid, FBX::SEPARATOR + "NodeAttribute", "LimbNode" node_attribute_uid, FBX::SEPARATOR + "NodeAttribute", "LimbNode"
); );
na.AddChild("TypeFlags", Property("Skeleton")); na.AddChild("TypeFlags", Property("Skeleton"));
na.Dump(outstream); na.Dump(outstream, binary, 1);
// and connect them // and connect them
FBX::Node c("C"); connections.emplace_back("C", "OO", node_attribute_uid, node_uid);
c.AddProperties("OO", node_attribute_uid, node_uid);
connections.push_back(c);
} else { } else {
// generate a null node so we can add children to it // 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 // if more than one child mesh, make nodes for each mesh
@ -2288,23 +2362,19 @@ void FBXExporter::WriteModelNodes(
// make a new model node // make a new model node
int64_t new_node_uid = generate_uid(); int64_t new_node_uid = generate_uid();
// connect to parent node // connect to parent node
FBX::Node c("C"); connections.emplace_back("C", "OO", new_node_uid, node_uid);
c.AddProperties("OO", new_node_uid, node_uid);
connections.push_back(c);
// connect to child mesh, which should have been written previously // connect to child mesh, which should have been written previously
c = FBX::Node("C"); connections.emplace_back(
c.AddProperties("OO", mesh_uids[node->mMeshes[i]], new_node_uid); "C", "OO", mesh_uids[node->mMeshes[i]], new_node_uid
connections.push_back(c); );
// also connect to the material for the child mesh // also connect to the material for the child mesh
c = FBX::Node("C"); connections.emplace_back(
c.AddProperties( "C", "OO",
"OO",
material_uids[ material_uids[
mScene->mMeshes[node->mMeshes[i]]->mMaterialIndex mScene->mMeshes[node->mMeshes[i]]->mMaterialIndex
], ],
new_node_uid new_node_uid
); );
connections.push_back(c);
// write model node // write model node
FBX::Node m("Model"); FBX::Node m("Model");
// take name from mesh name, if it exists // take name from mesh name, if it exists
@ -2315,7 +2385,7 @@ void FBXExporter::WriteModelNodes(
FBX::Node p("Properties70"); FBX::Node p("Properties70");
p.AddP70enum("InheritType", 1); p.AddP70enum("InheritType", 1);
m.AddChild(p); m.AddChild(p);
m.Dump(outstream); m.Dump(outstream, binary, 1);
} }
} }
@ -2344,15 +2414,11 @@ void FBXExporter::WriteAnimationCurveNode(
p.AddP70numberA("d|Y", default_value.y); p.AddP70numberA("d|Y", default_value.y);
p.AddP70numberA("d|Z", default_value.z); p.AddP70numberA("d|Z", default_value.z);
n.AddChild(p); n.AddChild(p);
n.Dump(outstream); n.Dump(outstream, binary, 1);
// connect to layer // connect to layer
FBX::Node cl("C"); this->connections.emplace_back("C", "OO", uid, layer_uid);
cl.AddProperties("OO", uid, layer_uid);
this->connections.push_back(cl); // TODO: emplace_back
// connect to bone // connect to bone
FBX::Node cb("C"); this->connections.emplace_back("C", "OP", uid, node_uid, property_name);
cb.AddProperties("OP", uid, node_uid, property_name);
this->connections.push_back(cb); // TODO: emplace_back
} }
@ -2379,10 +2445,10 @@ void FBXExporter::WriteAnimationCurve(
"KeyAttrRefCount", "KeyAttrRefCount",
std::vector<int32_t>{static_cast<int32_t>(times.size())} std::vector<int32_t>{static_cast<int32_t>(times.size())}
); );
n.Dump(outstream); n.Dump(outstream, binary, 1);
FBX::Node c("C"); this->connections.emplace_back(
c.AddProperties("OP", curve_uid, curvenode_uid, property_link); "C", "OP", curve_uid, curvenode_uid, property_link
this->connections.push_back(c); // TODO: emplace_back );
} }
@ -2390,13 +2456,18 @@ void FBXExporter::WriteConnections ()
{ {
// we should have completed the connection graph already, // we should have completed the connection graph already,
// so basically just dump it here // so basically just dump it here
if (!binary) {
WriteAsciiSectionHeader("Object connections");
}
// TODO: comments with names in the ascii version
FBX::Node conn("Connections"); FBX::Node conn("Connections");
StreamWriterLE outstream(outfile); StreamWriterLE outstream(outfile);
conn.Begin(outstream); conn.Begin(outstream, binary, 0);
conn.BeginChildren(outstream, binary, 0);
for (auto &n : connections) { 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(); connections.clear();
} }

View File

@ -48,6 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER #ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
#include "FBXExportNode.h" // FBX::Node #include "FBXExportNode.h" // FBX::Node
#include "FBXCommon.h" // FBX::TransformInheritance
#include <assimp/types.h> #include <assimp/types.h>
//#include <assimp/material.h> //#include <assimp/material.h>
@ -104,6 +105,9 @@ namespace Assimp
void WriteBinaryHeader(); void WriteBinaryHeader();
void WriteBinaryFooter(); void WriteBinaryFooter();
// ascii files have a comment at the top
void WriteAsciiHeader();
// WriteAllNodes does the actual export. // WriteAllNodes does the actual export.
// It just calls all the Write<Section> methods below in order. // It just calls all the Write<Section> methods below in order.
void WriteAllNodes(); void WriteAllNodes();
@ -126,6 +130,7 @@ namespace Assimp
// WriteTakes(); // deprecated since at least 2015 (fbx 7.4) // WriteTakes(); // deprecated since at least 2015 (fbx 7.4)
// helpers // helpers
void WriteAsciiSectionHeader(const std::string& title);
void WriteModelNodes( void WriteModelNodes(
Assimp::StreamWriterLE& s, Assimp::StreamWriterLE& s,
const aiNode* node, const aiNode* node,
@ -139,6 +144,15 @@ namespace Assimp
const std::unordered_set<const aiNode*>& limbnodes, const std::unordered_set<const aiNode*>& limbnodes,
std::vector<std::pair<std::string,aiVector3D>>& transform_chain 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( void WriteAnimationCurveNode(
StreamWriterLE& outstream, StreamWriterLE& outstream,
int64_t uid, int64_t uid,

View File

@ -316,11 +316,9 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref<Texture>& texture, aiTe
std::string path = tex.C_Str(); std::string path = tex.C_Str();
if (path.size() > 0) { if (path.size() > 0) {
if (path[0] != '*') { std::map<std::string, unsigned int>::iterator it = mTexturesByPath.find(path);
std::map<std::string, unsigned int>::iterator it = mTexturesByPath.find(path); if (it != mTexturesByPath.end()) {
if (it != mTexturesByPath.end()) { texture = mAsset->textures.Get(it->second);
texture = mAsset->textures.Get(it->second);
}
} }
if (!texture) { if (!texture) {

View File

@ -203,6 +203,12 @@ public:
Put(n); Put(n);
} }
// ---------------------------------------------------------------------
/** Write a single character to the stream */
void PutChar(char c) {
Put(c);
}
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
/** Write an aiString to the stream */ /** Write an aiString to the stream */
void PutString(const aiString& s) void PutString(const aiString& s)