update assimp to 6.0.5

This commit is contained in:
AzaezelX 2026-06-09 12:46:56 -05:00
parent 2d2eb57e2e
commit f5cf21cfeb
941 changed files with 22718 additions and 12240 deletions

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -143,31 +143,33 @@ AnimationCurveNode::AnimationCurveNode(uint64_t id, const Element &element, cons
// ------------------------------------------------------------------------------------------------
const AnimationCurveMap &AnimationCurveNode::Curves() const {
if (curves.empty()) {
// resolve attached animation curves
const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "AnimationCurve");
if (!curves.empty()) {
return curves;
}
for (const Connection *con : conns) {
// resolve attached animation curves
const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "AnimationCurve");
// link should go for a property
if (!con->PropertyName().length()) {
continue;
}
for (const Connection *con : conns) {
const Object *const ob = con->SourceObject();
if (nullptr == ob) {
DOMWarning("failed to read source object for AnimationCurve->AnimationCurveNode link, ignoring", &element);
continue;
}
const AnimationCurve *const anim = dynamic_cast<const AnimationCurve *>(ob);
if (nullptr == anim) {
DOMWarning("source object for ->AnimationCurveNode link is not an AnimationCurve", &element);
continue;
}
curves[con->PropertyName()] = anim;
// link should go for a property
if (!con->PropertyName().length()) {
continue;
}
const Object *const ob = con->SourceObject();
if (nullptr == ob) {
DOMWarning("failed to read source object for AnimationCurve->AnimationCurveNode link, ignoring", &element);
continue;
}
const AnimationCurve *const anim = dynamic_cast<const AnimationCurve *>(ob);
if (nullptr == anim) {
DOMWarning("source object for ->AnimationCurveNode link is not an AnimationCurve", &element);
continue;
}
curves[con->PropertyName()] = anim;
}
return curves;

View file

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -60,58 +59,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Assimp {
namespace FBX {
//enum Flag
//{
// e_unknown_0 = 1 << 0,
// e_unknown_1 = 1 << 1,
// e_unknown_2 = 1 << 2,
// e_unknown_3 = 1 << 3,
// e_unknown_4 = 1 << 4,
// e_unknown_5 = 1 << 5,
// e_unknown_6 = 1 << 6,
// e_unknown_7 = 1 << 7,
// e_unknown_8 = 1 << 8,
// e_unknown_9 = 1 << 9,
// e_unknown_10 = 1 << 10,
// e_unknown_11 = 1 << 11,
// e_unknown_12 = 1 << 12,
// e_unknown_13 = 1 << 13,
// e_unknown_14 = 1 << 14,
// e_unknown_15 = 1 << 15,
// e_unknown_16 = 1 << 16,
// e_unknown_17 = 1 << 17,
// e_unknown_18 = 1 << 18,
// e_unknown_19 = 1 << 19,
// e_unknown_20 = 1 << 20,
// e_unknown_21 = 1 << 21,
// e_unknown_22 = 1 << 22,
// e_unknown_23 = 1 << 23,
// e_flag_field_size_64_bit = 1 << 24, // Not sure what is
// e_unknown_25 = 1 << 25,
// e_unknown_26 = 1 << 26,
// e_unknown_27 = 1 << 27,
// e_unknown_28 = 1 << 28,
// e_unknown_29 = 1 << 29,
// e_unknown_30 = 1 << 30,
// e_unknown_31 = 1 << 31
//};
//
//bool check_flag(uint32_t flags, Flag to_check)
//{
// return (flags & to_check) != 0;
//}
// ------------------------------------------------------------------------------------------------
Token::Token(const char* sbegin, const char* send, TokenType type, size_t offset)
:
#ifdef DEBUG
contents(sbegin, static_cast<size_t>(send-sbegin)),
#endif
sbegin(sbegin)
, send(send)
, type(type)
, line(offset)
, column(BINARY_MARKER)
{
Token::Token(const char* sbegin, const char* send, TokenType type, size_t offset) :
#ifdef DEBUG
contents(sbegin, static_cast<size_t>(send-sbegin)),
#endif
sbegin(sbegin),
send(send),
type(type),
line(offset),
column(BINARY_MARKER) {
ai_assert(sbegin);
ai_assert(send);
@ -134,7 +91,9 @@ AI_WONT_RETURN void TokenizeError(const std::string& message, size_t offset)
// ------------------------------------------------------------------------------------------------
size_t Offset(const char* begin, const char* cursor) {
ai_assert(begin <= cursor);
if (begin > cursor) {
return 0;
}
return cursor - begin;
}

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -47,11 +47,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
namespace Assimp {
namespace FBX {
namespace Assimp::FBX {
static constexpr size_t NumNullRecords = 25;
const char NULL_RECORD[NumNullRecords] = { // 25 null bytes in 64-bit and 13 null bytes in 32-bit
constexpr char NULL_RECORD[NumNullRecords] = { // 25 null bytes in 64-bit and 13 null bytes in 32-bit
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'
}; // who knows why, it looks like two integers 32/64 bit (compressed and uncompressed sizes?) + 1 byte (might be compression type?)
@ -83,8 +83,7 @@ enum TransformInheritance {
TransformInheritance_MAX // end-of-enum sentinel
};
} // namespace FBX
} // namespace Assimp
} // namespace Assimp::FBX
#endif // ASSIMP_BUILD_NO_FBX_EXPORTER

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -49,7 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <set>
//
#if _MSC_VER > 1500 || (defined __GNUC___)
#if _MSC_VER > 1500 || (defined __GNUC__)
# define ASSIMP_FBX_USE_UNORDERED_MULTIMAP
# else
# define fbx_unordered_map map

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -237,7 +237,7 @@ std::string FBXConverter::MakeUniqueNodeName(const Model *const model, const aiN
/// When a node becomes a child of another node, that node becomes its owner and mOwnership should be released.
struct FBXConverter::PotentialNode {
PotentialNode() : mOwnership(new aiNode), mNode(mOwnership.get()) {}
PotentialNode(const std::string& name) : mOwnership(new aiNode(name)), mNode(mOwnership.get()) {}
explicit PotentialNode(const std::string& name) : mOwnership(new aiNode(name)), mNode(mOwnership.get()) {}
aiNode* operator->() { return mNode; }
std::unique_ptr<aiNode> mOwnership;
aiNode* mNode;
@ -438,7 +438,8 @@ void FBXConverter::ConvertLight(const Light &light, const std::string &orig_name
out_light->mType = aiLightSource_UNDEFINED;
break;
default:
ai_assert(false);
FBXImporter::LogError("Not handled light type: ", light.LightType());
break;
}
float decay = light.DecayStart();
@ -463,7 +464,7 @@ void FBXConverter::ConvertLight(const Light &light, const std::string &orig_name
out_light->mAttenuationQuadratic = 1.0f;
break;
default:
ai_assert(false);
FBXImporter::LogError("Not handled light decay type: ", light.DecayType());
break;
}
}
@ -601,7 +602,7 @@ const char *FBXConverter::NameTransformationCompProperty(TransformationComp comp
return "GeometricRotationInverse";
case TransformationComp_GeometricTranslationInverse:
return "GeometricTranslationInverse";
case TransformationComp_MAXIMUM: // this is to silence compiler warnings
case TransformationComp_MAXIMUM:
break;
}
@ -2957,7 +2958,7 @@ void FBXConverter::GenerateNodeAnimations(std::vector<aiNodeAnim *> &node_anims,
// be invoked _later_ (animations come first). If this node has only rotation,
// scaling and translation _and_ there are no animated other components either,
// we can use a single node and also a single node animation channel.
if( !has_complex && !NeedsComplexTransformationChain(target)) {
if (!doc.Settings().preservePivots || (!has_complex && !NeedsComplexTransformationChain(target))) {
aiNodeAnim* const nd = GenerateSimpleNodeAnim(fixed_name, target, chain,
node_property_map.end(),
start, stop,
@ -3415,7 +3416,7 @@ FBXConverter::KeyFrameListList FBXConverter::GetRotationKeyframeList(const std::
KeyFrameListList inputs;
inputs.reserve(nodes.size() * 3);
//give some breathing room for rounding errors
// give some breathing room for rounding errors
const int64_t adj_start = start - 10000;
const int64_t adj_stop = stop + 10000;
@ -3441,7 +3442,7 @@ FBXConverter::KeyFrameListList FBXConverter::GetRotationKeyframeList(const std::
ai_assert(curve->GetKeys().size() == curve->GetValues().size());
ai_assert(curve->GetKeys().size());
//get values within the start/stop time window
// get values within the start/stop time window
std::shared_ptr<KeyTimeList> Keys(new KeyTimeList());
std::shared_ptr<KeyValueList> Values(new KeyValueList());
const size_t count = curve->GetKeys().size();
@ -3461,8 +3462,7 @@ FBXConverter::KeyFrameListList FBXConverter::GetRotationKeyframeList(const std::
if (tnew >= adj_start && tnew <= adj_stop) {
Keys->push_back(tnew);
Values->push_back(vnew);
}
else {
} else {
// Something broke
break;
}

View file

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -70,7 +69,7 @@ struct morphKeyData {
std::vector<unsigned int> values;
std::vector<float> weights;
};
typedef std::map<int64_t, morphKeyData*> morphAnimData;
using morphAnimData = std::map<int64_t, morphKeyData*> ;
namespace Assimp {
namespace FBX {

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -45,6 +45,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
#include <algorithm>
#include "FBXParser.h"
#include "FBXDocument.h"
#include "FBXMeshGeometry.h"
@ -65,9 +67,6 @@ Deformer::Deformer(uint64_t id, const Element& element, const Document& doc, con
props = GetPropertyTable(doc,"Deformer.Fbx" + classname,element,sc,true);
}
// ------------------------------------------------------------------------------------------------
Deformer::~Deformer() = default;
// ------------------------------------------------------------------------------------------------
Cluster::Cluster(uint64_t id, const Element& element, const Document& doc, const std::string& name)
: Deformer(id,element,doc,name)
@ -113,10 +112,6 @@ Cluster::Cluster(uint64_t id, const Element& element, const Document& doc, const
}
}
// ------------------------------------------------------------------------------------------------
Cluster::~Cluster() = default;
// ------------------------------------------------------------------------------------------------
Skin::Skin(uint64_t id, const Element& element, const Document& doc, const std::string& name)
: Deformer(id,element,doc,name)
@ -142,9 +137,6 @@ Skin::Skin(uint64_t id, const Element& element, const Document& doc, const std::
}
}
// ------------------------------------------------------------------------------------------------
Skin::~Skin() = default;
// ------------------------------------------------------------------------------------------------
BlendShape::BlendShape(uint64_t id, const Element& element, const Document& doc, const std::string& name)
: Deformer(id, element, doc, name)
@ -154,15 +146,16 @@ BlendShape::BlendShape(uint64_t id, const Element& element, const Document& doc,
for (const Connection* con : conns) {
const BlendShapeChannel* const bspc = ProcessSimpleConnection<BlendShapeChannel>(*con, false, "BlendShapeChannel -> BlendShape", element);
if (bspc) {
auto pr = blendShapeChannels.insert(bspc);
if (!pr.second) {
// Only add a channel if it doesn't exist already
if (std::find(blendShapeChannels.begin(), blendShapeChannels.end(), bspc) == blendShapeChannels.end()) {
blendShapeChannels.push_back(bspc);
} else {
FBXImporter::LogWarn("there is the same blendShapeChannel id ", bspc->ID());
}
}
}
}
// ------------------------------------------------------------------------------------------------
BlendShape::~BlendShape() = default;
// ------------------------------------------------------------------------------------------------
BlendShapeChannel::BlendShapeChannel(uint64_t id, const Element& element, const Document& doc, const std::string& name)
: Deformer(id, element, doc, name)
@ -181,17 +174,17 @@ BlendShapeChannel::BlendShapeChannel(uint64_t id, const Element& element, const
for (const Connection* con : conns) {
const ShapeGeometry* const sg = ProcessSimpleConnection<ShapeGeometry>(*con, false, "Shape -> BlendShapeChannel", element);
if (sg) {
auto pr = shapeGeometries.insert(sg);
if (!pr.second) {
// Only add a geometry if it doesn't exist already
if (std::find(shapeGeometries.begin(), shapeGeometries.end(), sg) == shapeGeometries.end()) {
shapeGeometries.push_back(sg);
} else {
FBXImporter::LogWarn("there is the same shapeGeometrie id ", sg->ID());
}
}
}
}
// ------------------------------------------------------------------------------------------------
BlendShapeChannel::~BlendShapeChannel() = default;
// ------------------------------------------------------------------------------------------------
}
}
#endif
} // namespace FBX
} // Namespace Assimp
#endif // ASSIMP_BUILD_NO_FBX_IMPORTER

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -269,7 +269,7 @@ Document::~Document()
{
// The document does not own the memory for the following objects, but we need to call their d'tor
// so they can properly free memory like string members:
for (ObjectMap::value_type &v : objects) {
delete_LazyObject(v.second);
}
@ -663,6 +663,10 @@ LazyObject& Connection::LazyDestinationObject() const {
const Object* Connection::SourceObject() const {
LazyObject* const lazy = doc.GetObject(src);
ai_assert(lazy);
if (lazy == nullptr) {
return nullptr;
}
return lazy->Get();
}
@ -670,6 +674,10 @@ const Object* Connection::SourceObject() const {
const Object* Connection::DestinationObject() const {
LazyObject* const lazy = doc.GetObject(dest);
ai_assert(lazy);
if (lazy == nullptr) {
return nullptr;
}
return lazy->Get();
}

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -174,7 +174,7 @@ class NodeAttribute : public Object {
public:
NodeAttribute(uint64_t id, const Element& element, const Document& doc, const std::string& name);
virtual ~NodeAttribute() = default;
~NodeAttribute() override = default;
const PropertyTable& Props() const {
ai_assert(props.get());
@ -186,11 +186,11 @@ private:
};
/** DOM base class for FBX camera settings attached to a node */
class CameraSwitcher : public NodeAttribute {
class CameraSwitcher final : public NodeAttribute {
public:
CameraSwitcher(uint64_t id, const Element& element, const Document& doc, const std::string& name);
virtual ~CameraSwitcher() = default;
~CameraSwitcher() override= default;
int CameraID() const {
return cameraId;
@ -231,11 +231,11 @@ private:
/** DOM base class for FBX cameras attached to a node */
class Camera : public NodeAttribute {
class Camera final : public NodeAttribute {
public:
Camera(uint64_t id, const Element& element, const Document& doc, const std::string& name);
virtual ~Camera() = default;
~Camera() override = default;
fbx_simple_property(Position, aiVector3D, aiVector3D(0,0,0))
fbx_simple_property(UpVector, aiVector3D, aiVector3D(0,1,0))
@ -257,24 +257,24 @@ public:
};
/** DOM base class for FBX null markers attached to a node */
class Null : public NodeAttribute {
class Null final : public NodeAttribute {
public:
Null(uint64_t id, const Element& element, const Document& doc, const std::string& name);
virtual ~Null() = default;
~Null() override = default;
};
/** DOM base class for FBX limb node markers attached to a node */
class LimbNode : public NodeAttribute {
class LimbNode final : public NodeAttribute {
public:
LimbNode(uint64_t id, const Element& element, const Document& doc, const std::string& name);
virtual ~LimbNode() = default;
~LimbNode() override = default;
};
/** DOM base class for FBX lights attached to a node */
class Light : public NodeAttribute {
class Light final : public NodeAttribute {
public:
Light(uint64_t id, const Element& element, const Document& doc, const std::string& name);
virtual ~Light() = default;
~Light() override = default;
enum Type {
Type_Point,
@ -329,7 +329,7 @@ public:
};
/** DOM base class for FBX models (even though its semantics are more "node" than "model" */
class Model : public Object {
class Model final : public Object {
public:
enum RotOrder {
RotOrder_EulerXYZ = 0,
@ -354,7 +354,7 @@ public:
Model(uint64_t id, const Element& element, const Document& doc, const std::string& name);
virtual ~Model() = default;
~Model() override = default;
fbx_simple_property(QuaternionInterpolate, int, 0)
@ -477,11 +477,11 @@ private:
};
/** DOM class for generic FBX textures */
class Texture : public Object {
class Texture final : public Object {
public:
Texture(uint64_t id, const Element& element, const Document& doc, const std::string& name);
virtual ~Texture();
~Texture() override;
const std::string& Type() const {
return type;
@ -542,10 +542,10 @@ private:
};
/** DOM class for layered FBX textures */
class LayeredTexture : public Object {
class LayeredTexture final : public Object {
public:
LayeredTexture(uint64_t id, const Element& element, const Document& doc, const std::string& name);
virtual ~LayeredTexture();
~LayeredTexture() override;
// Can only be called after construction of the layered texture object due to construction flag.
void fillTexture(const Document& doc);
@ -608,11 +608,11 @@ using TextureMap = std::fbx_unordered_map<std::string, const Texture*>;
using LayeredTextureMap = std::fbx_unordered_map<std::string, const LayeredTexture*>;
/** DOM class for generic FBX videos */
class Video : public Object {
class Video final : public Object {
public:
Video(uint64_t id, const Element& element, const Document& doc, const std::string& name);
virtual ~Video();
~Video() override;
const std::string& Type() const {
return type;
@ -657,11 +657,11 @@ private:
};
/** DOM class for generic FBX materials */
class Material : public Object {
class Material final : public Object {
public:
Material(uint64_t id, const Element& element, const Document& doc, const std::string& name);
virtual ~Material();
~Material() override = default;
const std::string& GetShadingModel() const {
return shading;
@ -697,10 +697,10 @@ using KeyTimeList = std::vector<int64_t>;
using KeyValueList = std::vector<float>;
/** Represents a FBX animation curve (i.e. a 1-dimensional set of keyframes and values therefore) */
class AnimationCurve : public Object {
class AnimationCurve final : public Object {
public:
AnimationCurve(uint64_t id, const Element& element, const std::string& name, const Document& doc);
virtual ~AnimationCurve() = default;
~AnimationCurve() override = default;
/** get list of keyframe positions (time).
* Invariant: |GetKeys()| > 0 */
@ -733,7 +733,7 @@ private:
using AnimationCurveMap = std::map<std::string, const AnimationCurve*>;
/** Represents a FBX animation curve (i.e. a mapping from single animation curves to nodes) */
class AnimationCurveNode : public Object {
class AnimationCurveNode final : public Object {
public:
/* the optional white list specifies a list of property names for which the caller
wants animations for. If the curve node does not match one of these, std::range_error
@ -741,7 +741,7 @@ public:
AnimationCurveNode(uint64_t id, const Element& element, const std::string& name, const Document& doc,
const char *const *target_prop_whitelist = nullptr, size_t whitelist_size = 0);
virtual ~AnimationCurveNode() = default;
~AnimationCurveNode() override = default;
const PropertyTable& Props() const {
ai_assert(props.get());
@ -783,7 +783,7 @@ private:
using AnimationCurveNodeList = std::vector<const AnimationCurveNode*>;
/** Represents a FBX animation layer (i.e. a list of node animations) */
class AnimationLayer : public Object {
class AnimationLayer final : public Object {
public:
AnimationLayer(uint64_t id, const Element& element, const std::string& name, const Document& doc);
virtual ~AnimationLayer() = default;
@ -806,7 +806,7 @@ private:
using AnimationLayerList = std::vector<const AnimationLayer*>;
/** Represents a FBX animation stack (i.e. a list of animation layers) */
class AnimationStack : public Object {
class AnimationStack final : public Object {
public:
AnimationStack(uint64_t id, const Element& element, const std::string& name, const Document& doc);
virtual ~AnimationStack() = default;
@ -835,7 +835,7 @@ private:
class Deformer : public Object {
public:
Deformer(uint64_t id, const Element& element, const Document& doc, const std::string& name);
virtual ~Deformer();
virtual ~Deformer() = default;
const PropertyTable& Props() const {
ai_assert(props.get());
@ -851,11 +851,11 @@ using WeightIndexArray = std::vector<unsigned int>;
/** DOM class for BlendShapeChannel deformers */
class BlendShapeChannel : public Deformer {
class BlendShapeChannel final : public Deformer {
public:
BlendShapeChannel(uint64_t id, const Element& element, const Document& doc, const std::string& name);
virtual ~BlendShapeChannel();
virtual ~BlendShapeChannel() = default;
float DeformPercent() const {
return percent;
@ -865,37 +865,37 @@ public:
return fullWeights;
}
const std::unordered_set<const ShapeGeometry*>& GetShapeGeometries() const {
const std::vector<const ShapeGeometry*>& GetShapeGeometries() const {
return shapeGeometries;
}
private:
float percent;
WeightArray fullWeights;
std::unordered_set<const ShapeGeometry*> shapeGeometries;
std::vector<const ShapeGeometry*> shapeGeometries;
};
/** DOM class for BlendShape deformers */
class BlendShape : public Deformer {
class BlendShape final : public Deformer {
public:
BlendShape(uint64_t id, const Element& element, const Document& doc, const std::string& name);
virtual ~BlendShape();
virtual ~BlendShape() = default;
const std::unordered_set<const BlendShapeChannel*>& BlendShapeChannels() const {
const std::vector<const BlendShapeChannel*>& BlendShapeChannels() const {
return blendShapeChannels;
}
private:
std::unordered_set<const BlendShapeChannel*> blendShapeChannels;
std::vector<const BlendShapeChannel*> blendShapeChannels;
};
/** DOM class for skin deformer clusters (aka sub-deformers) */
class Cluster : public Deformer {
class Cluster final : public Deformer {
public:
Cluster(uint64_t id, const Element& element, const Document& doc, const std::string& name);
virtual ~Cluster();
virtual ~Cluster() = default;
/** get the list of deformer weights associated with this cluster.
* Use #GetIndices() to get the associated vertices. Both arrays
@ -935,11 +935,11 @@ private:
};
/** DOM class for skin deformers */
class Skin : public Deformer {
class Skin final : public Deformer {
public:
Skin(uint64_t id, const Element& element, const Document& doc, const std::string& name);
virtual ~Skin();
virtual ~Skin() = default;
float DeformAccuracy() const {
return accuracy;

View file

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -36,7 +35,6 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
@ -81,8 +79,7 @@ void DOMWarning(const std::string& message, const Token& token) {
}
// ------------------------------------------------------------------------------------------------
void DOMWarning(const std::string& message, const Element* element /*= nullptr*/)
{
void DOMWarning(const std::string& message, const Element* element /*= nullptr*/) {
if(element) {
DOMWarning(message,element->KeyToken());
return;
@ -92,41 +89,39 @@ void DOMWarning(const std::string& message, const Element* element /*= nullptr*/
}
}
// ------------------------------------------------------------------------------------------------
// fetch a property table and the corresponding property template
std::shared_ptr<const PropertyTable> GetPropertyTable(const Document& doc,
const std::string& templateName,
const Element &element,
const Scope& sc,
bool no_warn /*= false*/)
{
const std::string& templateName,
const Element &element,
const Scope& sc,
bool no_warn /*= false*/) {
const Element* const Properties70 = sc["Properties70"];
std::shared_ptr<const PropertyTable> templateProps = std::shared_ptr<const PropertyTable>(
static_cast<const PropertyTable *>(nullptr));
if(templateName.length()) {
if (templateName.length()) {
PropertyTemplateMap::const_iterator it = doc.Templates().find(templateName);
if(it != doc.Templates().end()) {
templateProps = (*it).second;
}
}
if(!Properties70 || !Properties70->Compound()) {
if (!Properties70 || !Properties70->Compound()) {
if(!no_warn) {
DOMWarning("property table (Properties70) not found",&element);
}
if(templateProps) {
return templateProps;
}
else {
} else {
return std::make_shared<const PropertyTable>();
}
}
return std::make_shared<const PropertyTable>(*Properties70,templateProps);
}
} // !Util
} // !FBX
} // !Assimp
#endif
#endif // ASSIMP_BUILD_NO_FBX_IMPORTER

View file

@ -114,4 +114,4 @@ inline const T* ProcessSimpleConnection(const Connection& con,
} //!FBX
} //!Assimp
#endif
#endif // ASSIMP_BUILD_NO_FBX_IMPORTER

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -35,7 +35,6 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
#ifndef ASSIMP_BUILD_NO_EXPORT
@ -55,37 +54,31 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <memory> // shared_ptr
namespace Assimp {
// AddP70<type> helpers... there's no usable pattern here,
// so all are defined as separate functions.
// Even "animatable" properties are often completely different
// from the standard (nonanimated) property definition,
// so they are specified with an 'A' suffix.
void FBX::Node::AddP70int(
const std::string& cur_name, int32_t value
) {
void FBX::Node::AddP70int(const std::string& cur_name, int32_t value) {
FBX::Node n("P");
n.AddProperties(cur_name, "int", "Integer", "", value);
AddChild(n);
}
void FBX::Node::AddP70bool(
const std::string& cur_name, bool value
) {
void FBX::Node::AddP70bool(const std::string& cur_name, bool value) {
FBX::Node n("P");
n.AddProperties(cur_name, "bool", "", "", int32_t(value));
AddChild(n);
}
void FBX::Node::AddP70double(
const std::string &cur_name, double value) {
FBX::Node n("P");
void FBX::Node::AddP70double(const std::string &cur_name, double value) { FBX::Node n("P");
n.AddProperties(cur_name, "double", "Number", "", value);
AddChild(n);
}
void FBX::Node::AddP70numberA(
const std::string &cur_name, double value) {
void FBX::Node::AddP70numberA(const std::string &cur_name, double value) {
FBX::Node n("P");
n.AddProperties(cur_name, "Number", "", "A", value);
AddChild(n);
@ -405,8 +398,7 @@ void FBX::Node::DumpChildrenAscii(std::ostream& s, int indent)
}
}
void FBX::Node::EndAscii(std::ostream& s, int indent, bool has_children)
{
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'; }
@ -417,11 +409,10 @@ void FBX::Node::EndAscii(std::ostream& s, int indent, bool has_children)
// 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
){
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);
@ -556,6 +547,8 @@ void FBX::Node::WritePropertyNode(
FBX::Node::WritePropertyNodeAscii(name, v, s, indent);
}
}
}
} // namespace Assimp
#endif // ASSIMP_BUILD_NO_FBX_EXPORTER
#endif // ASSIMP_BUILD_NO_EXPORT

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -35,7 +35,6 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
@ -70,7 +69,6 @@ public:
// some nodes always pretend they have children...
bool force_has_children = false;
public: // constructors
/// The default class constructor.
Node() = default;
@ -89,7 +87,6 @@ public: // constructors
AddProperties(std::forward<More>(more)...);
}
public: // functions to add properties or children
// add a single property to the node
template <typename T>
void AddProperty(T&& value) {
@ -118,8 +115,6 @@ public: // functions to add properties or children
children.push_back(std::move(c));
}
public: // support specifically for dealing with Properties70 nodes
// it really is simpler to make these all separate functions.
// the versions with 'A' suffixes are for animatable properties.
// those often follow a completely different format internally in FBX.
@ -150,8 +145,6 @@ public: // support specifically for dealing with Properties70 nodes
AddChild(n);
}
public: // member functions for writing data to a file or stream
// write the full node to the given file or stream
void Dump(
const std::shared_ptr<Assimp::IOStream> &outfile,
@ -175,31 +168,6 @@ public: // member functions for writing data to a file or stream
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
size_t end_pos; // ending position in stream
size_t property_start; // starting position of property section
public: // static member functions
// convenience function to create a node with a single property,
// and write it to the stream.
@ -235,7 +203,26 @@ public: // static member functions
bool binary, int indent
);
private: // static helper functions
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);
// static helper functions
static void WritePropertyNodeAscii(
const std::string& name,
const std::vector<double>& v,
@ -259,9 +246,13 @@ private: // static helper functions
Assimp::StreamWriterLE& s
);
private: // data used for binary dumps
size_t start_pos; // starting position in stream
size_t end_pos; // ending position in stream
size_t property_start; // starting position of property section
};
}
} // Namespace Assimp
#endif // ASSIMP_BUILD_NO_FBX_EXPORTER
#endif // AI_FBXEXPORTNODE_H_INC

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -286,6 +286,8 @@ void FBXExportProperty::DumpAscii(std::ostream& s, int indent) {
break;
}
}
// assimp issue #6112; fallthrough confirmed by @mesilliac
[[fallthrough]];
case 'R':
s << '"';
// we might as well check this now,

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -51,6 +51,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/IOSystem.hpp>
#include <assimp/Exporter.hpp>
#include <assimp/DefaultLogger.hpp>
#include <assimp/Logger.hpp>
#include <assimp/StreamWriter.h> // StreamWriterLE
#include <assimp/Exceptional.h> // DeadlyExportError
#include <assimp/material.h> // aiTextureType
@ -370,12 +371,6 @@ void FBXExporter::WriteHeaderExtension ()
"Creator", creator.str(), outstream, binary, indent
);
//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, binary, indent);
indent = 0;
// finish node
@ -459,11 +454,7 @@ void WritePropString(const aiScene* scene, FBX::Node& p, const std::string& key,
}
}
void FBXExporter::WriteGlobalSettings ()
{
if (!binary) {
// no title, follows directly from the header extension
}
void FBXExporter::WriteGlobalSettings () {
FBX::Node gs("GlobalSettings");
gs.AddChild("Version", int32_t(1000));
@ -493,8 +484,7 @@ void FBXExporter::WriteGlobalSettings ()
gs.Dump(outfile, binary, 0);
}
void FBXExporter::WriteDocuments ()
{
void FBXExporter::WriteDocuments() {
if (!binary) {
WriteAsciiSectionHeader("Documents Description");
}
@ -523,8 +513,7 @@ void FBXExporter::WriteDocuments ()
docs.Dump(outfile, binary, 0);
}
void FBXExporter::WriteReferences ()
{
void FBXExporter::WriteReferences() {
if (!binary) {
WriteAsciiSectionHeader("Document References");
}
@ -540,7 +529,6 @@ void FBXExporter::WriteReferences ()
// some internal helper functions used for writing the definitions
// (before any actual data is written)
// ---------------------------------------------------------------
size_t count_nodes(const aiNode* n, const aiNode* root) {
size_t count;
if (n == root) {
@ -556,8 +544,7 @@ size_t count_nodes(const aiNode* n, const aiNode* root) {
return count;
}
bool has_phong_mat(const aiScene* scene)
{
static bool has_phong_mat(const aiScene* scene) {
// just search for any material with a shininess exponent
for (size_t i = 0; i < scene->mNumMaterials; ++i) {
aiMaterial* mat = scene->mMaterials[i];
@ -570,16 +557,12 @@ bool has_phong_mat(const aiScene* scene)
return false;
}
size_t count_images(const aiScene* scene) {
static size_t count_images(const aiScene* scene) {
std::unordered_set<std::string> images;
aiString texpath;
for (size_t i = 0; i < scene->mNumMaterials; ++i) {
aiMaterial* mat = scene->mMaterials[i];
for (
size_t tt = aiTextureType_DIFFUSE;
tt < aiTextureType_UNKNOWN;
++tt
){
aiMaterial *mat = scene->mMaterials[i];
for (size_t tt = aiTextureType_DIFFUSE; tt < aiTextureType_UNKNOWN; ++tt) {
const aiTextureType textype = static_cast<aiTextureType>(tt);
const size_t texcount = mat->GetTextureCount(textype);
for (unsigned int j = 0; j < texcount; ++j) {
@ -588,10 +571,11 @@ size_t count_images(const aiScene* scene) {
}
}
}
return images.size();
}
size_t count_textures(const aiScene* scene) {
static size_t count_textures(const aiScene* scene) {
size_t count = 0;
for (size_t i = 0; i < scene->mNumMaterials; ++i) {
aiMaterial* mat = scene->mMaterials[i];
@ -609,7 +593,7 @@ size_t count_textures(const aiScene* scene) {
return count;
}
size_t count_deformers(const aiScene* scene) {
static size_t count_deformers(const aiScene* scene) {
size_t count = 0;
for (size_t i = 0; i < scene->mNumMeshes; ++i) {
const size_t n = scene->mMeshes[i]->mNumBones;
@ -621,8 +605,7 @@ size_t count_deformers(const aiScene* scene) {
return count;
}
void FBXExporter::WriteDefinitions ()
{
void FBXExporter::WriteDefinitions () {
// basically this is just bookkeeping:
// determining how many of each type of object there are
// and specifying the base properties to use when otherwise unspecified.
@ -1033,9 +1016,7 @@ void FBXExporter::WriteDefinitions ()
// some internal helper functions used for writing the objects section
// (which holds the actual data)
// -------------------------------------------------------------------
aiNode* get_node_for_mesh(unsigned int meshIndex, aiNode* node)
{
static aiNode* get_node_for_mesh(unsigned int meshIndex, aiNode* node) {
for (size_t i = 0; i < node->mNumMeshes; ++i) {
if (node->mMeshes[i] == meshIndex) {
return node;
@ -1048,8 +1029,7 @@ aiNode* get_node_for_mesh(unsigned int meshIndex, aiNode* node)
return nullptr;
}
aiMatrix4x4 get_world_transform(const aiNode* node, const aiScene* scene)
{
aiMatrix4x4 get_world_transform(const aiNode* node, const aiScene* scene) {
std::vector<const aiNode*> node_chain;
while (node != scene->mRootNode && node != nullptr) {
node_chain.push_back(node);
@ -1063,18 +1043,47 @@ aiMatrix4x4 get_world_transform(const aiNode* node, const aiScene* scene)
}
inline int64_t to_ktime(double ticks, const aiAnimation* anim) {
if (FP_ZERO == std::fpclassify(anim->mTicksPerSecond)) {
return static_cast<int64_t>(ticks) * FBX::SECOND;
if (FP_ZERO == std::fpclassify(anim->mTicksPerSecond)) {
return static_cast<int64_t>(ticks * FBX::SECOND);
}
return (static_cast<int64_t>(ticks / anim->mTicksPerSecond)) * FBX::SECOND;
// Defensive: handle zero or near-zero mTicksPerSecond
double tps = anim->mTicksPerSecond;
double timeVal;
if (FP_ZERO == std::fpclassify(tps)) {
timeVal = ticks;
} else {
timeVal = ticks / tps;
}
// Clamp to prevent overflow
const double kMax = static_cast<double>(INT64_MAX) / static_cast<double>(FBX::SECOND);
const double kMin = static_cast<double>(INT64_MIN) / static_cast<double>(FBX::SECOND);
if (timeVal > kMax) {
return INT64_MAX;
}
if (timeVal < kMin) {
return INT64_MIN;
}
return static_cast<int64_t>((ticks / anim->mTicksPerSecond) * FBX::SECOND);
}
inline int64_t to_ktime(double time) {
return (static_cast<int64_t>(time * FBX::SECOND));
// Clamp to prevent overflow
const double kMax = static_cast<double>(INT64_MAX) / static_cast<double>(FBX::SECOND);
const double kMin = static_cast<double>(INT64_MIN) / static_cast<double>(FBX::SECOND);
if (time > kMax) {
return INT64_MAX;
}
if (time < kMin) {
return INT64_MIN;
}
return static_cast<int64_t>(time * FBX::SECOND);
}
void FBXExporter::WriteObjects ()
{
void FBXExporter::WriteObjects () {
if (!binary) {
WriteAsciiSectionHeader("Object properties");
}
@ -1087,21 +1096,27 @@ void FBXExporter::WriteObjects ()
object_node.BeginChildren(outstream, binary, indent);
bool bJoinIdenticalVertices = mProperties->GetPropertyBool("bJoinIdenticalVertices", true);
std::vector<std::vector<int32_t>> vVertexIndice;//save vertex_indices as it is needed later
// save vertex_indices as it is needed later
std::vector<std::vector<int32_t>> vVertexIndice(mScene->mNumMeshes);
std::vector<uint32_t> uniq_v_before_mi;
const auto bTransparencyFactorReferencedToOpacity = mProperties->GetPropertyBool(AI_CONFIG_EXPORT_FBX_TRANSPARENCY_FACTOR_REFER_TO_OPACITY, false);
// 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];
std::function<void(const aiNode*)> visit_node_geo = [&](const aiNode *node) {
if (node->mNumMeshes == 0) {
for (uint32_t ni = 0; ni < node->mNumChildren; ni++) {
visit_node_geo(node->mChildren[ni]);
}
return;
}
// start the node record
FBX::Node n("Geometry");
int64_t uid = generate_uid();
mesh_uids.push_back(uid);
mesh_uids[node] = uid;
n.AddProperty(uid);
n.AddProperty(FBX::SEPARATOR + "Geometry");
n.AddProperty("Mesh");
@ -1109,159 +1124,113 @@ void FBXExporter::WriteObjects ()
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;
// index of original vertex in vertex data vector
std::vector<int32_t> vertex_indices;
// map of vertex value to its index in the data vector
std::map<aiVector3D,size_t> index_by_vertex_value;
if(bJoinIdenticalVertices){
int32_t index = 0;
for (size_t vi = 0; vi < m->mNumVertices; ++vi) {
aiVector3D vtx = m->mVertices[vi];
auto elem = index_by_vertex_value.find(vtx);
if (elem == index_by_vertex_value.end()) {
vertex_indices.push_back(index);
index_by_vertex_value[vtx] = index;
flattened_vertices.push_back(vtx[0]);
flattened_vertices.push_back(vtx[1]);
flattened_vertices.push_back(vtx[2]);
++index;
} else {
vertex_indices.push_back(int32_t(elem->second));
}
}
}
else { // do not join vertex, respect the export flag
vertex_indices.resize(m->mNumVertices);
std::iota(vertex_indices.begin(), vertex_indices.end(), 0);
for(unsigned int v = 0; v < m->mNumVertices; ++ v) {
aiVector3D vtx = m->mVertices[v];
flattened_vertices.push_back(vtx.x);
flattened_vertices.push_back(vtx.y);
flattened_vertices.push_back(vtx.z);
}
}
vVertexIndice.push_back(vertex_indices);
FBX::Node::WritePropertyNode(
"Vertices", flattened_vertices, outstream, binary, indent
);
std::vector<double> normal_data;
std::vector<double> color_data;
std::vector<int32_t> polygon_data;
std::vector<std::vector<double>> uv_data;
std::vector<std::vector<int32_t>> uv_indices;
indent = 2;
for (uint32_t n_mi = 0; n_mi < node->mNumMeshes; n_mi++) {
const auto mi = node->mMeshes[n_mi];
const aiMesh *m = mScene->mMeshes[mi];
size_t v_offset = vertex_indices.size();
size_t uniq_v_before = flattened_vertices.size() / 3;
// map of vertex value to its index in the data vector
std::map<aiVector3D,size_t> index_by_vertex_value;
if (bJoinIdenticalVertices) {
int32_t index = 0;
for (size_t vi = 0; vi < m->mNumVertices; ++vi) {
aiVector3D vtx = m->mVertices[vi];
auto elem = index_by_vertex_value.find(vtx);
if (elem == index_by_vertex_value.end()) {
vertex_indices.push_back(index);
index_by_vertex_value[vtx] = index;
flattened_vertices.insert(flattened_vertices.end(), { vtx.x, vtx.y, vtx.z });
++index;
} else {
vertex_indices.push_back(int32_t(elem->second));
}
}
} else { // do not join vertex, respect the export flag
vertex_indices.resize(v_offset + m->mNumVertices);
std::iota(vertex_indices.begin() + v_offset, vertex_indices.end(), 0);
for(unsigned int v = 0; v < m->mNumVertices; ++ v) {
aiVector3D vtx = m->mVertices[v];
flattened_vertices.insert(flattened_vertices.end(), {vtx.x, vtx.y, vtx.z});
}
}
vVertexIndice[mi].insert(
// TODO test whether this can be end or not
vVertexIndice[mi].end(),
vertex_indices.begin() + v_offset,
vertex_indices.end()
);
// here could be edges but they're insane.
// it's optional anyway, so let's ignore it.
// output polygon data as a flattened array of vertex indices.
// the last vertex index of each polygon is negated and - 1
std::vector<int32_t> polygon_data;
for (size_t fi = 0; fi < m->mNumFaces; ++fi) {
for (size_t fi = 0; fi < m->mNumFaces; fi++) {
const aiFace &f = m->mFaces[fi];
for (size_t pvi = 0; pvi < f.mNumIndices - 1; ++pvi) {
polygon_data.push_back(vertex_indices[f.mIndices[pvi]]);
if (f.mNumIndices == 0) continue;
size_t pvi = 0;
for (; pvi < f.mNumIndices - 1; pvi++) {
polygon_data.push_back(
static_cast<int32_t>(uniq_v_before + vertex_indices[v_offset + f.mIndices[pvi]])
);
}
polygon_data.push_back(
-1 - vertex_indices[f.mIndices[f.mNumIndices-1]]
static_cast<int32_t>(-1 ^ (uniq_v_before + vertex_indices[v_offset+f.mIndices[pvi]]))
);
}
FBX::Node::WritePropertyNode(
"PolygonVertexIndex", polygon_data, outstream, binary, indent
);
}
// here could be edges but they're insane.
// it's optional anyway, so let's ignore it.
uniq_v_before_mi.push_back(static_cast<uint32_t>(uniq_v_before));
FBX::Node::WritePropertyNode(
"GeometryVersion", int32_t(124), outstream, binary, indent
);
// normals, if any
if (m->HasNormals()) {
FBX::Node normals("LayerElementNormal", int32_t(0));
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(
"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, binary, indent
);
std::vector<double> normal_data;
if (m->HasNormals()) {
normal_data.reserve(3 * polygon_data.size());
for (size_t fi = 0; fi < m->mNumFaces; ++fi) {
const aiFace &f = m->mFaces[fi];
for (size_t pvi = 0; pvi < f.mNumIndices; ++pvi) {
const aiVector3D &curN = m->mNormals[f.mIndices[pvi]];
normal_data.push_back(curN.x);
normal_data.push_back(curN.y);
normal_data.push_back(curN.z);
}
for (size_t fi = 0; fi < m->mNumFaces; fi++) {
const aiFace & f = m->mFaces[fi];
for (size_t pvi = 0; pvi < f.mNumIndices; pvi++) {
const aiVector3D &curN = m->mNormals[f.mIndices[pvi]];
normal_data.insert(normal_data.end(), { curN.x, curN.y, curN.z });
}
}
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.
indent = 2;
normals.End(outstream, binary, indent, true);
}
}
// colors, if any
for (size_t ci = 0; ci < m->GetNumColorChannels(); ++ci) {
FBX::Node vertexcolors("LayerElementColor", int32_t(ci));
vertexcolors.Begin(outstream, binary, indent);
vertexcolors.DumpProperties(outstream, binary, indent);
vertexcolors.EndProperties(outstream, binary, indent);
vertexcolors.BeginChildren(outstream, binary, indent);
indent = 3;
FBX::Node::WritePropertyNode(
"Version", int32_t(101), outstream, binary, indent
);
char layerName[8];
snprintf(layerName, sizeof(layerName), "COLOR_%d", int32_t(ci));
FBX::Node::WritePropertyNode(
"Name", (const char*)layerName, outstream, binary, indent
);
FBX::Node::WritePropertyNode(
"MappingInformationType", "ByPolygonVertex",
outstream, binary, indent
);
FBX::Node::WritePropertyNode(
"ReferenceInformationType", "Direct",
outstream, binary, indent
);
std::vector<double> color_data;
const int32_t colorChannelIndex = 0;
if (m->HasVertexColors(colorChannelIndex)) {
color_data.reserve(4 * polygon_data.size());
for (size_t fi = 0; fi < m->mNumFaces; ++fi) {
const aiFace &f = m->mFaces[fi];
for (size_t pvi = 0; pvi < f.mNumIndices; ++pvi) {
const aiColor4D &c = m->mColors[ci][f.mIndices[pvi]];
color_data.push_back(c.r);
color_data.push_back(c.g);
color_data.push_back(c.b);
color_data.push_back(c.a);
}
for (size_t fi = 0; fi < m->mNumFaces; fi++) {
const aiFace &f = m->mFaces[fi];
for (size_t pvi = 0; pvi < f.mNumIndices; pvi++) {
const aiColor4D &c = m->mColors[colorChannelIndex][f.mIndices[pvi]];
color_data.insert(color_data.end(), { c.r, c.g, c.b, c.a });
}
}
FBX::Node::WritePropertyNode(
"Colors", color_data, outstream, binary, indent
);
indent = 2;
vertexcolors.End(outstream, binary, indent, true);
}
}
// uvs, if any
for (size_t uvi = 0; uvi < m->GetNumUVChannels(); ++uvi) {
if (m->mNumUVComponents[uvi] > 2) {
const auto num_uv = static_cast<size_t>(m->GetNumUVChannels());
uv_indices.resize(std::max(num_uv, uv_indices.size()));
uv_data.resize(std::max(num_uv, uv_data.size()));
std::map<aiVector3D, int32_t> index_by_uv;
// uvs, if any
for (size_t uvi = 0; uvi < m->GetNumUVChannels(); uvi++) {
const auto nc = m->mNumUVComponents[uvi];
if (nc > 2) {
// FBX only supports 2-channel UV maps...
// or at least i'm not sure how to indicate a different number
std::stringstream err;
@ -1276,71 +1245,112 @@ void FBXExporter::WriteObjects ()
err << " but may be incorrectly interpreted on load.";
ASSIMP_LOG_WARN(err.str());
}
FBX::Node uv("LayerElementUV", int32_t(uvi));
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, binary, indent
);
FBX::Node::WritePropertyNode(
"MappingInformationType", "ByPolygonVertex",
outstream, binary, indent
);
FBX::Node::WritePropertyNode(
"ReferenceInformationType", "IndexToDirect",
outstream, binary, indent
);
std::vector<double> uv_data;
std::vector<int32_t> uv_indices;
std::map<aiVector3D,int32_t> index_by_uv;
int32_t index = 0;
for (size_t fi = 0; fi < m->mNumFaces; ++fi) {
const aiFace &f = m->mFaces[fi];
for (size_t pvi = 0; pvi < f.mNumIndices; ++pvi) {
const aiVector3D &curUv =
m->mTextureCoords[uvi][f.mIndices[pvi]];
auto elem = index_by_uv.find(curUv);
if (elem == index_by_uv.end()) {
index_by_uv[curUv] = index;
uv_indices.push_back(index);
for (unsigned int x = 0; x < m->mNumUVComponents[uvi]; ++x) {
uv_data.push_back(curUv[x]);
}
++index;
} else {
uv_indices.push_back(elem->second);
}
int32_t index = static_cast<int32_t>(uv_data[uvi].size()) / nc;
for (size_t fi = 0; fi < m->mNumFaces; fi++) {
const aiFace &f = m->mFaces[fi];
for (size_t pvi = 0; pvi < f.mNumIndices; pvi++) {
const aiVector3D &curUv = m->mTextureCoords[uvi][f.mIndices[pvi]];
auto elem = index_by_uv.find(curUv);
if (elem == index_by_uv.end()) {
index_by_uv[curUv] = index;
uv_indices[uvi].push_back(index);
for (uint32_t x = 0; x < nc; ++x) {
uv_data[uvi].push_back(curUv[x]);
}
++index;
} else {
uv_indices[uvi].push_back(elem->second);
}
}
}
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,
// as the material is linked via "Connections".
// it seems to always have the same "0" value.
FBX::Node::WritePropertyNode("Vertices", flattened_vertices, outstream, binary, indent);
FBX::Node::WritePropertyNode("PolygonVertexIndex", polygon_data, outstream, binary, indent);
FBX::Node::WritePropertyNode("GeometryVersion", int32_t(124), outstream, binary, indent);
if (!normal_data.empty()) {
FBX::Node normals("LayerElementNormal", int32_t(0));
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("Version", int32_t(101), outstream, binary, indent);
FBX::Node::WritePropertyNode("Name", "", outstream, binary, indent);
FBX::Node::WritePropertyNode("MappingInformationType", "ByPolygonVertex", outstream, binary, indent);
FBX::Node::WritePropertyNode("ReferenceInformationType", "Direct", outstream, binary, indent);
FBX::Node::WritePropertyNode("Normals", normal_data, outstream, binary, indent);
// note: version 102 has a NormalsW also... not sure what it is,
// so stick with version 101 for now.
indent = 2;
normals.End(outstream, binary, indent, true);
}
if (!color_data.empty()) {
const auto colorChannelIndex = 0;
FBX::Node vertexcolors("LayerElementColor", int32_t(colorChannelIndex));
vertexcolors.Begin(outstream, binary, indent);
vertexcolors.DumpProperties(outstream, binary, indent);
vertexcolors.EndProperties(outstream, binary, indent);
vertexcolors.BeginChildren(outstream, binary, indent);
indent = 3;
FBX::Node::WritePropertyNode("Version", int32_t(101), outstream, binary, indent);
char layerName[8];
snprintf(layerName, sizeof(layerName), "COLOR_%d", colorChannelIndex);
FBX::Node::WritePropertyNode("Name", (const char *)layerName, outstream, binary, indent);
FBX::Node::WritePropertyNode("MappingInformationType", "ByPolygonVertex", outstream, binary, indent);
FBX::Node::WritePropertyNode("ReferenceInformationType", "Direct", outstream, binary, indent);
FBX::Node::WritePropertyNode("Colors", color_data, outstream, binary, indent);
indent = 2;
vertexcolors.End(outstream, binary, indent, true);
}
for (uint32_t uvi = 0; uvi < uv_data.size(); uvi++) {
FBX::Node uv("LayerElementUV", int32_t(uvi));
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);
FBX::Node::WritePropertyNode("Name", "", outstream, binary, indent);
FBX::Node::WritePropertyNode("MappingInformationType", "ByPolygonVertex", outstream, binary, indent);
FBX::Node::WritePropertyNode("ReferenceInformationType", "IndexToDirect", outstream, binary, indent);
FBX::Node::WritePropertyNode("UV", uv_data[uvi], outstream, binary, indent);
FBX::Node::WritePropertyNode("UVIndex", uv_indices[uvi], outstream, binary, indent);
indent = 2;
uv.End(outstream, binary, indent, true);
}
// When merging multiple meshes, we instead use by polygon so the correct material is
// assigned to each face. Previously, this LayerElementMaterial always had 0 since it
// assumed there was 1 material for each node for all meshes.
FBX::Node mat("LayerElementMaterial", int32_t(0));
mat.AddChild("Version", int32_t(101));
mat.AddChild("Name", "");
mat.AddChild("MappingInformationType", "AllSame");
mat.AddChild("ReferenceInformationType", "IndexToDirect");
std::vector<int32_t> mat_indices = {0};
mat.AddChild("Materials", mat_indices);
if (node->mNumMeshes == 1) {
mat.AddChild("MappingInformationType", "AllSame");
mat.AddChild("ReferenceInformationType", "IndexToDirect");
std::vector<int32_t> mat_indices = {0};
mat.AddChild("Materials", mat_indices);
} else {
mat.AddChild("MappingInformationType", "ByPolygon");
mat.AddChild("ReferenceInformationType", "IndexToDirect");
std::vector<int32_t> mat_indices;
for (uint32_t n_mi = 0; n_mi < node->mNumMeshes; n_mi++) {
const auto mi = node->mMeshes[n_mi];
const auto *const m = mScene->mMeshes[mi];
for (size_t fi = 0; fi < m->mNumFaces; fi++) {
mat_indices.push_back(n_mi);
}
}
mat.AddChild("Materials", mat_indices);
}
mat.Dump(outstream, binary, indent);
// finally we have the layer specifications,
@ -1348,16 +1358,20 @@ void FBXExporter::WriteObjects ()
// TODO: handle multiple uv sets correctly?
FBX::Node layer("Layer", int32_t(0));
layer.AddChild("Version", int32_t(100));
FBX::Node le("LayerElement");
le.AddChild("Type", "LayerElementNormal");
le.AddChild("TypedIndex", int32_t(0));
layer.AddChild(le);
FBX::Node le;
for (size_t ci = 0; ci < m->GetNumColorChannels(); ++ci) {
le = FBX::Node("LayerElement");
le.AddChild("Type", "LayerElementColor");
le.AddChild("TypedIndex", int32_t(ci));
layer.AddChild(le);
if (!normal_data.empty()) {
le = FBX::Node("LayerElement");
le.AddChild("Type", "LayerElementNormal");
le.AddChild("TypedIndex", int32_t(0));
layer.AddChild(le);
}
if (!color_data.empty()) {
le = FBX::Node("LayerElement");
le.AddChild("Type", "LayerElementColor");
le.AddChild("TypedIndex", int32_t(0));
layer.AddChild(le);
}
le = FBX::Node("LayerElement");
@ -1370,8 +1384,7 @@ void FBXExporter::WriteObjects ()
layer.AddChild(le);
layer.Dump(outstream, binary, indent);
for(unsigned int lr = 1; lr < m->GetNumUVChannels(); ++ lr)
{
for(unsigned int lr = 1; lr < uv_data.size(); ++ lr) {
FBX::Node layerExtra("Layer", int32_t(lr));
layerExtra.AddChild("Version", int32_t(100));
FBX::Node leExtra("LayerElement");
@ -1383,7 +1396,14 @@ void FBXExporter::WriteObjects ()
// finish the node record
indent = 1;
n.End(outstream, binary, indent, true);
}
for (uint32_t ni = 0; ni < node->mNumChildren; ni++) {
visit_node_geo(node->mChildren[ni]);
}
return;
};
visit_node_geo(mScene->mRootNode);
// aiMaterial
@ -1562,6 +1582,7 @@ void FBXExporter::WriteObjects ()
}
}
std::map<std::string, std::string> tpath_by_image;
// FbxVideo - stores images used by textures.
for (const auto &it : uid_by_image) {
FBX::Node n("Video");
@ -1581,10 +1602,21 @@ void FBXExporter::WriteObjects ()
std::stringstream newPath;
if (embedded_texture->mFilename.length > 0) {
newPath << embedded_texture->mFilename.C_Str();
// If newPath doesn't end in an extension, add extension from embedded_texture->achFormatHint
std::string np = newPath.str();
size_t dot_pos = np.find_last_of('.');
if (dot_pos == std::string::npos || dot_pos < np.find_last_of("/\\")) {
// No extension found, add one
newPath << "." << embedded_texture->achFormatHint;
}
} else if (embedded_texture->achFormatHint[0]) {
int texture_index = std::stoi(path.substr(1, path.size() - 1));
newPath << texture_index << "." << embedded_texture->achFormatHint;
}
auto elem = tpath_by_image.find(path);
if (elem == tpath_by_image.end()) {
tpath_by_image[path] = newPath.str();
}
path = newPath.str();
// embed the texture
size_t texture_size = static_cast<size_t>(embedded_texture->mWidth * std::max(embedded_texture->mHeight, 1u));
@ -1703,6 +1735,17 @@ void FBXExporter::WriteObjects ()
unsigned int max = sizeof(aiUVTransform);
aiGetMaterialFloatArray(mat, AI_MATKEY_UVTRANSFORM(aiTextureType_DIFFUSE, 0), (ai_real *)&trafo, &max);
auto tp_elem = tpath_by_image.find(texture_path);
std::string tfile_path = texture_path;
if (tp_elem != tpath_by_image.end()) {
tfile_path = tp_elem->second;
} else {
std::stringstream err;
err << "Texture path not found for texure " << texture_path;
err << " on material " << i;
ASSIMP_LOG_WARN(err.str());
}
// now write the actual texture node
FBX::Node tnode("Texture");
// TODO: some way to determine texture name?
@ -1723,8 +1766,8 @@ void FBXExporter::WriteObjects ()
// can't easily determine which texture path will be correct,
// so just store what we have in every field.
// these being incorrect is a common problem with FBX anyway.
tnode.AddChild("FileName", texture_path);
tnode.AddChild("RelativeFilename", texture_path);
tnode.AddChild("FileName", tfile_path);
tnode.AddChild("RelativeFilename", tfile_path);
tnode.AddChild("ModelUVTranslation", double(0.0), double(0.0));
tnode.AddChild("ModelUVScaling", double(1.0), double(1.0));
tnode.AddChild("Texture_Alpha_Source", "None");
@ -1748,7 +1791,8 @@ void FBXExporter::WriteObjects ()
dnode.AddChild("Version", int32_t(101));
dnode.Dump(outstream, binary, indent);
// connect it
connections.emplace_back("C", "OO", deformer_uid, mesh_uids[mi]);
const auto node = get_node_for_mesh((unsigned int)mi, mScene->mRootNode);
connections.emplace_back("C", "OO", deformer_uid, mesh_uids[node]);
std::vector<int32_t> vertex_indices = vVertexIndice[mi];
for (unsigned int am = 0; am < m->mNumAnimMeshes; ++am) {
@ -1758,7 +1802,7 @@ void FBXExporter::WriteObjects ()
// start the node record
FBX::Node bsnode("Geometry");
int64_t blendshape_uid = generate_uid();
mesh_uids.push_back(blendshape_uid);
blendshape_uids.push_back(blendshape_uid);
bsnode.AddProperty(blendshape_uid);
bsnode.AddProperty(blendshape_name + FBX::SEPARATOR + "Geometry");
bsnode.AddProperty("Shape");
@ -1945,22 +1989,15 @@ void FBXExporter::WriteObjects ()
// otherwise check if this is the root of the skeleton
bool end = false;
// is the mesh part of this node?
for (size_t i = 0; i < parent->mNumMeshes; ++i) {
if (parent->mMeshes[i] == mi) {
end = true;
break;
}
for (size_t i = 0; i < parent->mNumMeshes && !end; ++i) {
end |= parent->mMeshes[i] == mi;
}
// is the mesh in one of the children of this node?
for (size_t j = 0; j < parent->mNumChildren; ++j) {
for (size_t j = 0; j < parent->mNumChildren && !end; ++j) {
aiNode* child = parent->mChildren[j];
for (size_t i = 0; i < child->mNumMeshes; ++i) {
if (child->mMeshes[i] == mi) {
end = true;
break;
}
for (size_t i = 0; i < child->mNumMeshes && !end; ++i) {
end |= child->mMeshes[i] == mi;
}
if (end) { break; }
}
// if it was the skeleton root we can finish here
@ -1974,8 +2011,7 @@ void FBXExporter::WriteObjects ()
for (size_t i = 0; i < mScene->mNumMeshes; ++i) {
auto &s = skeleton_by_mesh[i];
for (const aiNode* n : s) {
auto elem = node_uids.find(n);
if (elem == node_uids.end()) {
if (node_uids.find(n) == node_uids.end()) {
node_uids[n] = generate_uid();
}
}
@ -1991,6 +2027,8 @@ void FBXExporter::WriteObjects ()
if (!m->HasBones()) {
continue;
}
const aiNode *mesh_node = get_node_for_mesh((uint32_t)mi, mScene->mRootNode);
// make a deformer for this mesh
int64_t deformer_uid = generate_uid();
FBX::Node dnode("Deformer");
@ -2002,10 +2040,7 @@ void FBXExporter::WriteObjects ()
dnode.Dump(outstream, binary, indent);
// connect it
connections.emplace_back("C", "OO", deformer_uid, mesh_uids[mi]);
//computed before
std::vector<int32_t>& vertex_indices = vVertexIndice[mi];
connections.emplace_back("C", "OO", deformer_uid, mesh_uids[mesh_node]);
// TODO, FIXME: this won't work if anything is not in the bind pose.
// for now if such a situation is detected, we throw an exception.
@ -2019,7 +2054,6 @@ void FBXExporter::WriteObjects ()
// as it can be instanced to many nodes.
// All we can do is assume no instancing,
// and take the first node we find that contains the mesh.
aiNode* mesh_node = get_node_for_mesh((unsigned int)mi, mScene->mRootNode);
aiMatrix4x4 mesh_xform = get_world_transform(mesh_node, mScene);
// now make a subdeformer for each bone in the skeleton
@ -2048,14 +2082,19 @@ void FBXExporter::WriteObjects ()
sdnode.AddChild("Version", int32_t(100));
sdnode.AddChild("UserData", "", "");
std::set<int32_t> setWeightedVertex;
// add indices and weights, if any
if (b) {
std::set<int32_t> setWeightedVertex;
std::vector<int32_t> subdef_indices;
std::vector<double> subdef_weights;
int32_t last_index = -1;
for (size_t wi = 0; wi < b->mNumWeights; ++wi) {
int32_t vi = vertex_indices[b->mWeights[wi].mVertexId];
if (b->mWeights[wi].mVertexId >= vVertexIndice[mi].size()) {
ASSIMP_LOG_ERROR("UNREAL: Skipping vertex index to prevent buffer overflow.");
continue;
}
int32_t vi = vVertexIndice[mi][b->mWeights[wi].mVertexId]
+ uniq_v_before_mi[mi];
bool bIsWeightedAlready = (setWeightedVertex.find(vi) != setWeightedVertex.end());
if (vi == last_index || bIsWeightedAlready) {
// only for vertices we exported to fbx
@ -2535,9 +2574,9 @@ void add_meta(FBX::Node& fbx_node, const aiNode* node){
default:
break;
}
}
}
// write a single model node to the stream
@ -2702,9 +2741,8 @@ void FBXExporter::WriteModelNodes(
// handled later
} else if (node->mNumMeshes == 1) {
// connect to child mesh, which should have been written previously
connections.emplace_back(
"C", "OO", mesh_uids[node->mMeshes[0]], node_uid
);
// TODO double check this line
connections.emplace_back("C", "OO", mesh_uids[node], node_uid);
// also connect to the material for the child mesh
connections.emplace_back(
"C", "OO",
@ -2729,6 +2767,16 @@ void FBXExporter::WriteModelNodes(
na.Dump(outstream, binary, 1);
// and connect them
connections.emplace_back("C", "OO", node_attribute_uid, node_uid);
} else if (node->mNumMeshes >= 1) {
connections.emplace_back("C", "OO", mesh_uids[node], node_uid);
for (size_t i = 0; i < node->mNumMeshes; i++) {
connections.emplace_back(
"C", "OO",
material_uids[mScene->mMeshes[node->mMeshes[i]]->mMaterialIndex],
node_uid
);
}
WriteModelNode(outstream, binary, node, node_uid, "Mesh", transform_chain);
} else {
const auto& lightIt = lights_uids.find(node->mName.C_Str());
if(lightIt != lights_uids.end()) {
@ -2745,34 +2793,20 @@ void FBXExporter::WriteModelNodes(
}
}
// if more than one child mesh, make nodes for each mesh
if (node->mNumMeshes > 1 || node == mScene->mRootNode) {
for (size_t i = 0; i < node->mNumMeshes; ++i) {
// make a new model node
int64_t new_node_uid = generate_uid();
// connect to parent node
connections.emplace_back("C", "OO", new_node_uid, node_uid);
// connect to child mesh, which should have been written previously
connections.emplace_back(
"C", "OO", mesh_uids[node->mMeshes[i]], new_node_uid
);
// also connect to the material for the child mesh
connections.emplace_back(
"C", "OO",
material_uids[
mScene->mMeshes[node->mMeshes[i]]->mMaterialIndex
],
new_node_uid
);
aiNode new_node;
// take name from mesh name, if it exists
new_node.mName = mScene->mMeshes[node->mMeshes[i]]->mName;
// write model node
WriteModelNode(
outstream, binary, &new_node, new_node_uid, "Mesh", std::vector<std::pair<std::string,aiVector3D>>()
);
}
if (node == mScene->mRootNode && node->mNumMeshes > 0) {
int64_t new_node_uid = generate_uid();
connections.emplace_back("C", "OO", new_node_uid, node_uid);
connections.emplace_back("C", "OO", mesh_uids[node], new_node_uid);
for (size_t i = 0; i < node->mNumMeshes; ++i) {
connections.emplace_back(
"C", "OO",
material_uids[mScene->mMeshes[node->mMeshes[i]]->mMaterialIndex],
new_node_uid
);
}
aiNode new_node;
new_node.mName = mScene->mMeshes[0]->mName;
WriteModelNode(outstream, binary, &new_node, new_node_uid, "Mesh", {});
}
// now recurse into children

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -51,7 +51,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "FBXCommon.h" // FBX::TransformInheritance
#include <assimp/types.h>
//#include <assimp/material.h>
#include <assimp/StreamWriter.h> // StreamWriterLE
#include <assimp/Exceptional.h> // DeadlyExportError
@ -91,7 +90,8 @@ namespace Assimp {
std::vector<FBX::Node> connections; // connection storage
std::vector<int64_t> mesh_uids;
std::map<const aiNode*, int64_t> mesh_uids;
std::vector<int64_t> blendshape_uids;
std::vector<int64_t> material_uids;
std::map<const aiNode*,int64_t> node_uids;
std::map<std::string,int64_t> lights_uids;

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -90,7 +90,7 @@ namespace {
// Returns whether the class can handle the format of the given file.
bool FBXImporter::CanRead(const std::string & pFile, IOSystem * pIOHandler, bool /*checkSig*/) const {
// at least ASCII-FBX files usually have a 'FBX' somewhere in their head
static const char *tokens[] = { " \n\r\n " };
static const char *tokens[] = { " \n\r\n ", "fbx" };
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
}

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -67,7 +67,7 @@ typedef class basic_formatter<char, std::char_traits<char>, std::allocator<char>
///
/// See http://en.wikipedia.org/wiki/FBX
// -------------------------------------------------------------------------------------------
class FBXImporter : public BaseImporter, public LogFunctions<FBXImporter> {
class FBXImporter final : public BaseImporter, public LogFunctions<FBXImporter> {
public:
/// @brief The class constructor.
FBXImporter() = default;

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -134,10 +134,6 @@ Material::Material(uint64_t id, const Element& element, const Document& doc, con
}
}
// ------------------------------------------------------------------------------------------------
Material::~Material() = default;
// ------------------------------------------------------------------------------------------------
Texture::Texture(uint64_t id, const Element& element, const Document& doc, const std::string& name) :
Object(id,element,name),

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
#include <algorithm>
#include <functional>
#include "FBXMeshGeometry.h"
@ -69,8 +70,10 @@ Geometry::Geometry(uint64_t id, const Element& element, const std::string& name,
}
const BlendShape* const bsp = ProcessSimpleConnection<BlendShape>(*con, false, "BlendShape -> Geometry", element);
if (bsp) {
auto pr = blendShapes.insert(bsp);
if (!pr.second) {
// Only add a blendshape if it doesn't exist already
if (std::find(blendShapes.begin(), blendShapes.end(), bsp) == blendShapes.end()) {
blendShapes.push_back(bsp);
} else {
FBXImporter::LogWarn("there is the same blendShape id ", bsp->ID());
}
}
@ -78,7 +81,7 @@ Geometry::Geometry(uint64_t id, const Element& element, const std::string& name,
}
// ------------------------------------------------------------------------------------------------
const std::unordered_set<const BlendShape*>& Geometry::GetBlendShapes() const {
const std::vector<const BlendShape*>& Geometry::GetBlendShapes() const {
return blendShapes;
}

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -72,11 +72,11 @@ public:
/// @brief Get the BlendShape attached to this geometry or nullptr
/// @return The blendshape arrays.
const std::unordered_set<const BlendShape*>& GetBlendShapes() const;
const std::vector<const BlendShape*>& GetBlendShapes() const;
private:
const Skin* skin;
std::unordered_set<const BlendShape*> blendShapes;
std::vector<const BlendShape*> blendShapes;
};

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.

View file

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -365,10 +365,14 @@ float ParseTokenAsFloat(const Token& t, const char*& err_out)
}
if (data[0] == 'F') {
return SafeParse<float>(data+1, t.end());
BE_NCONST float id = SafeParse<float>(data+1, t.end());
AI_SWAP4(id);
return id;
}
else {
return static_cast<float>( SafeParse<double>(data+1, t.end()) );
BE_NCONST double id = SafeParse<double>(data+1, t.end());
AI_SWAP8(id);
return static_cast<float>(id);
}
}
@ -637,9 +641,15 @@ void ParseVectorDataArray(std::vector<aiVector3D>& out, const Element& el)
if (type == 'd') {
const double* d = reinterpret_cast<const double*>(&buff[0]);
for (unsigned int i = 0; i < count3; ++i, d += 3) {
out.emplace_back(static_cast<ai_real>(d[0]),
static_cast<ai_real>(d[1]),
static_cast<ai_real>(d[2]));
BE_NCONST double val1 = d[0];
BE_NCONST double val2 = d[1];
BE_NCONST double val3 = d[2];
AI_SWAP8(val1);
AI_SWAP8(val2);
AI_SWAP8(val3);
out.emplace_back(static_cast<ai_real>(val1),
static_cast<ai_real>(val2),
static_cast<ai_real>(val3));
}
// for debugging
/*for ( size_t i = 0; i < out.size(); i++ ) {
@ -652,7 +662,13 @@ void ParseVectorDataArray(std::vector<aiVector3D>& out, const Element& el)
else if (type == 'f') {
const float* f = reinterpret_cast<const float*>(&buff[0]);
for (unsigned int i = 0; i < count3; ++i, f += 3) {
out.emplace_back(f[0],f[1],f[2]);
BE_NCONST float val1 = f[0];
BE_NCONST float val2 = f[1];
BE_NCONST float val3 = f[2];
AI_SWAP4(val1);
AI_SWAP4(val2);
AI_SWAP4(val3);
out.emplace_back(val1,val2,val3);
}
}
@ -726,16 +742,32 @@ void ParseVectorDataArray(std::vector<aiColor4D>& out, const Element& el)
if (type == 'd') {
const double* d = reinterpret_cast<const double*>(&buff[0]);
for (unsigned int i = 0; i < count4; ++i, d += 4) {
out.emplace_back(static_cast<float>(d[0]),
static_cast<float>(d[1]),
static_cast<float>(d[2]),
static_cast<float>(d[3]));
BE_NCONST double val1 = d[0];
BE_NCONST double val2 = d[1];
BE_NCONST double val3 = d[2];
BE_NCONST double val4 = d[3];
AI_SWAP8(val1);
AI_SWAP8(val2);
AI_SWAP8(val3);
AI_SWAP8(val4);
out.emplace_back(static_cast<float>(val1),
static_cast<float>(val2),
static_cast<float>(val3),
static_cast<float>(val4));
}
}
else if (type == 'f') {
const float* f = reinterpret_cast<const float*>(&buff[0]);
for (unsigned int i = 0; i < count4; ++i, f += 4) {
out.emplace_back(f[0],f[1],f[2],f[3]);
BE_NCONST float val1 = f[0];
BE_NCONST float val2 = f[1];
BE_NCONST float val3 = f[2];
BE_NCONST float val4 = f[3];
AI_SWAP4(val1);
AI_SWAP4(val2);
AI_SWAP4(val3);
AI_SWAP4(val4);
out.emplace_back(val1,val2,val3,val4);
}
}
return;
@ -807,13 +839,21 @@ void ParseVectorDataArray(std::vector<aiVector2D>& out, const Element& el) {
if (type == 'd') {
const double* d = reinterpret_cast<const double*>(&buff[0]);
for (unsigned int i = 0; i < count2; ++i, d += 2) {
out.emplace_back(static_cast<float>(d[0]),
static_cast<float>(d[1]));
BE_NCONST double val1 = d[0];
BE_NCONST double val2 = d[1];
AI_SWAP8(val1);
AI_SWAP8(val2);
out.emplace_back(static_cast<float>(val1),
static_cast<float>(val2));
}
} else if (type == 'f') {
const float* f = reinterpret_cast<const float*>(&buff[0]);
for (unsigned int i = 0; i < count2; ++i, f += 2) {
out.emplace_back(f[0],f[1]);
BE_NCONST float val1 = f[0];
BE_NCONST float val2 = f[1];
AI_SWAP4(val1);
AI_SWAP4(val2);
out.emplace_back(val1,val2);
}
}
@ -938,13 +978,17 @@ void ParseVectorDataArray(std::vector<float>& out, const Element& el)
if (type == 'd') {
const double* d = reinterpret_cast<const double*>(&buff[0]);
for (unsigned int i = 0; i < count; ++i, ++d) {
out.push_back(static_cast<float>(*d));
BE_NCONST double val = *d;
AI_SWAP8(val);
out.push_back(static_cast<float>(val));
}
}
else if (type == 'f') {
const float* f = reinterpret_cast<const float*>(&buff[0]);
for (unsigned int i = 0; i < count; ++i, ++f) {
out.push_back(*f);
BE_NCONST float val = *f;
AI_SWAP4(val);
out.push_back(val);
}
}

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -64,7 +64,7 @@ class Parser;
class Element;
using ScopeList = std::vector<Scope*>;
using ElementMap = std::fbx_unordered_multimap< std::string, Element*>;
using ElementMap = std::multimap< std::string, Element*>;
using ElementCollection = std::pair<ElementMap::const_iterator,ElementMap::const_iterator>;
#define new_Scope new (allocator.Allocate(sizeof(Scope))) Scope
@ -82,10 +82,9 @@ using ElementCollection = std::pair<ElementMap::const_iterator,ElementMap::const
* @endverbatim
*
* As can be seen in this sample, elements can contain nested #Scope
* as their trailing member.
* as their trailing member.
**/
class Element
{
class Element {
public:
Element(const Token& key_token, Parser& parser);
~Element();

View file

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -57,15 +56,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Assimp {
namespace FBX {
using namespace Util;
using namespace Util;
// ------------------------------------------------------------------------------------------------
Property::Property() = default;
// ------------------------------------------------------------------------------------------------
Property::~Property() = default;
namespace {
namespace {
void checkTokenCount(const TokenList &tok, unsigned int expectedCount) {
ai_assert(expectedCount >= 2);
@ -146,9 +141,9 @@ Property* ReadTypedProperty(const Element& element)
// ------------------------------------------------------------------------------------------------
// peek into an element and check if it contains a FBX property, if so return its name.
std::string PeekPropertyName(const Element& element)
{
std::string PeekPropertyName(const Element& element) {
ai_assert(element.KeyToken().StringContents() == "P");
const TokenList& tok = element.Tokens();
if(tok.size() < 4) {
return std::string();
@ -160,13 +155,6 @@ std::string PeekPropertyName(const Element& element)
} //! anon
// ------------------------------------------------------------------------------------------------
PropertyTable::PropertyTable()
: templateProps()
, element()
{
}
// ------------------------------------------------------------------------------------------------
PropertyTable::PropertyTable(const Element &element, std::shared_ptr<const PropertyTable> templateProps) :
templateProps(std::move(templateProps)), element(&element) {

View file

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -56,34 +55,33 @@ namespace FBX {
// Forward declarations
class Element;
/** Represents a dynamic property. Type info added by deriving classes,
* see #TypedProperty.
Example:
@verbatim
P: "ShininessExponent", "double", "Number", "",0.5
@endvebatim
*/
/**
* Represents a dynamic property. Type info added by deriving classes,
* see #TypedProperty.
* Example:
*
* @verbatim
* P: "ShininessExponent", "double", "Number", "",0.5
* @endvebatim
*/
class Property {
protected:
Property();
public:
virtual ~Property();
virtual ~Property() = default;
public:
template <typename T>
const T* As() const {
return dynamic_cast<const T*>(this);
}
protected:
Property() = default;
};
template<typename T>
class TypedProperty : public Property {
public:
explicit TypedProperty(const T& value)
: value(value) {
// empty
}
explicit TypedProperty(const T& value) : value(value) {}
const T& Value() const {
return value;
@ -93,10 +91,9 @@ private:
T value;
};
typedef std::fbx_unordered_map<std::string,std::shared_ptr<Property> > DirectPropertyMap;
typedef std::fbx_unordered_map<std::string,const Property*> PropertyMap;
typedef std::fbx_unordered_map<std::string,const Element*> LazyPropertyMap;
using DirectPropertyMap = std::fbx_unordered_map<std::string,std::shared_ptr<Property> >;
using PropertyMap = std::fbx_unordered_map<std::string,const Property*>;
using LazyPropertyMap = std::fbx_unordered_map<std::string,const Element*>;
/**
* Represents a property table as can be found in the newer FBX files (Properties60, Properties70)
@ -104,7 +101,7 @@ typedef std::fbx_unordered_map<std::string,const Element*> LazyPrope
class PropertyTable {
public:
// in-memory property table with no source element
PropertyTable();
PropertyTable() : element() {}
PropertyTable(const Element& element, std::shared_ptr<const PropertyTable> templateProps);
~PropertyTable();
@ -130,8 +127,7 @@ private:
// ------------------------------------------------------------------------------------------------
template <typename T>
inline
T PropertyGet(const PropertyTable& in, const std::string& name, const T& defaultValue) {
inline T PropertyGet(const PropertyTable& in, const std::string& name, const T& defaultValue) {
const Property* const prop = in.Get(name);
if( nullptr == prop) {
return defaultValue;
@ -148,8 +144,7 @@ T PropertyGet(const PropertyTable& in, const std::string& name, const T& default
// ------------------------------------------------------------------------------------------------
template <typename T>
inline
T PropertyGet(const PropertyTable& in, const std::string& name, bool& result, bool useTemplate=false ) {
inline T PropertyGet(const PropertyTable& in, const std::string& name, bool& result, bool useTemplate=false ) {
const Property* prop = in.Get(name);
if( nullptr == prop) {
if ( ! useTemplate ) {

View file

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.

View file

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.

View file

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -51,11 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <string>
#include <cstring>
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
namespace Assimp {
namespace FBX {
namespace Util {
namespace Assimp::FBX::Util {
// ------------------------------------------------------------------------------------------------
const char* TokenTypeString(TokenType t)
@ -234,8 +229,5 @@ std::string EncodeBase64(const char* data, size_t length)
return encoded_string;
}
} // !Util
} // !FBX
} // !Assimp
} // namespace Assimp::FBX::Util
#endif

View file

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -50,12 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "FBXTokenizer.h"
#include <stdint.h>
namespace Assimp {
namespace FBX {
namespace Util {
namespace Assimp::FBX::Util {
/** helper for std::for_each to delete all heap-allocated items in a container */
template<typename T>
@ -76,19 +70,15 @@ struct destructor_fun {
}
};
/** Get a string representation for a #TokenType. */
const char* TokenTypeString(TokenType t);
/** Format log/error messages using a given offset in the source binary file
*
* @param offset offset within the file
* @return A string of the following format: " (offset 0x{offset}) "*/
std::string GetOffsetText(size_t offset);
/** Format log/error messages using a given line location in the source file.
*
* @param line Line index, 1-based
@ -96,7 +86,6 @@ std::string GetOffsetText(size_t offset);
* @return A string of the following format: " (line {line}, col {column}) "*/
std::string GetLineAndColumnText(unsigned int line, unsigned int column);
/** Format log/error messages using a given cursor token.
*
* @param tok Token where parsing/processing stopped
@ -134,8 +123,6 @@ char EncodeBase64(char byte);
* @return base64-encoded string*/
std::string EncodeBase64(const char* data, size_t length);
}
}
}
#endif // ! INCLUDED_AI_FBX_UTIL_H