This stops using FBXPropertyTable as a pointer.

The base object will inherit the property table, for every FBX object, if it doesn't exist it will be ignored.

The previous code was dangerous and not simple to understand, this makes the code simpler and should result in no leaks with PropertyTable.

Features/Fixes:

Adds ability for multiple millions of polygons to be loaded.
Fixes memory leaks with tokens
Fixes memory leaks with property table
Fixes loading some corrupt files
Fixes meshes not having a unique name to the mesh node.
Opens up loading for two more versions: 7100 and 7200, up to 2020.
Preliminary support for Cinema4D files in parser now, before this was not possible it would cause memory corruption, which is gone now.

FBXProperties not being pointers presented simpler challenges in the long run also, fixed a bunch of bugs.
This commit is contained in:
Gordon MacPherson 2021-03-30 02:33:06 +01:00
parent a86e7c3bb7
commit 061b77e5e6
21 changed files with 221 additions and 375 deletions

View file

@ -277,7 +277,7 @@ Ref<StandardMaterial3D> FBXMaterial::import_material(ImportState &state) {
}
/// ALL below is related to properties
for (FBXDocParser::LazyPropertyMap::value_type iter : material->Props()->GetLazyProperties()) {
for (FBXDocParser::LazyPropertyMap::value_type iter : material->GetLazyProperties()) {
const std::string name = iter.first;
if (name.empty()) {
@ -317,7 +317,7 @@ Ref<StandardMaterial3D> FBXMaterial::import_material(ImportState &state) {
ERR_CONTINUE_MSG(desc == PROPERTY_DESC_NOT_FOUND, "The FBX material parameter: `" + String(name.c_str()) + "` was not recognized. Please open an issue so we can add the support to it.");
const FBXDocParser::PropertyTable *tbl = material->Props();
const FBXDocParser::PropertyTable *tbl = material;
FBXDocParser::PropertyPtr prop = tbl->Get(name);
ERR_CONTINUE_MSG(prop == nullptr, "This file may be corrupted because is not possible to extract the material parameter: " + String(name.c_str()));

View file

@ -101,20 +101,6 @@ HashMap<int, Vector2> collect_uv(const Vector<VertexData<Vector2>> *p_data, Hash
return collection;
}
typedef int Vertex;
typedef int SurfaceId;
typedef int PolygonId;
typedef int DataIndex;
struct SurfaceData {
Ref<SurfaceTool> surface_tool;
OrderedHashMap<Vertex, int> lookup_table; // proposed fix is to replace lookup_table[vertex_id] to give the position of the vertices_map[int] index.
LocalVector<Vertex> vertices_map; // this must be ordered the same as insertion <-- slow to do find() operation.
Ref<Material> material;
HashMap<PolygonId, Vector<DataIndex>> surface_polygon_vertex;
Array morphs;
};
EditorSceneImporterMeshNode3D *FBXMeshData::create_fbx_mesh(const ImportState &state, const FBXDocParser::MeshGeometry *p_mesh_geometry, const FBXDocParser::Model *model, bool use_compression) {
mesh_geometry = p_mesh_geometry;
// todo: make this just use a uint64_t FBX ID this is a copy of our original materials unfortunately.
@ -307,11 +293,9 @@ EditorSceneImporterMeshNode3D *FBXMeshData::create_fbx_mesh(const ImportState &s
// Triangulate the various polygons and add the indices.
for (const PolygonId *polygon_id = surface->surface_polygon_vertex.next(nullptr); polygon_id != nullptr; polygon_id = surface->surface_polygon_vertex.next(polygon_id)) {
const Vector<DataIndex> *indices = surface->surface_polygon_vertex.getptr(*polygon_id);
triangulate_polygon(
surface->surface_tool,
surface,
*indices,
surface->vertices_map,
vertices);
}
}
@ -336,7 +320,7 @@ EditorSceneImporterMeshNode3D *FBXMeshData::create_fbx_mesh(const ImportState &s
morph_st->begin(Mesh::PRIMITIVE_TRIANGLES);
for (unsigned int vi = 0; vi < surface->vertices_map.size(); vi += 1) {
const Vertex vertex = surface->vertices_map[vi];
const Vertex &vertex = surface->vertices_map[vi];
add_vertex(
state,
morph_st,
@ -398,6 +382,9 @@ EditorSceneImporterMeshNode3D *FBXMeshData::create_fbx_mesh(const ImportState &s
EditorSceneImporterMeshNode3D *godot_mesh = memnew(EditorSceneImporterMeshNode3D);
godot_mesh->set_mesh(mesh);
const String name = ImportUtils::FBXNodeToName(model->Name());
godot_mesh->set_name(name); // hurry up compiling >.<
mesh->set_name("mesh3d-" + name);
return godot_mesh;
}
@ -816,8 +803,10 @@ void FBXMeshData::add_vertex(
p_surface_tool->add_vertex((p_vertices_position[p_vertex] + p_morph_value) * p_scale);
}
void FBXMeshData::triangulate_polygon(Ref<SurfaceTool> st, Vector<int> p_polygon_vertex, const Vector<Vertex> p_surface_vertex_map, const std::vector<Vector3> &p_vertices) const {
void FBXMeshData::triangulate_polygon(SurfaceData *surface, const Vector<int> &p_polygon_vertex, const std::vector<Vector3> &p_vertices) const {
Ref<SurfaceTool> st(surface->surface_tool);
const int polygon_vertex_count = p_polygon_vertex.size();
//const Vector<Vertex>& p_surface_vertex_map
if (polygon_vertex_count == 1) {
// point to triangle
st->add_index(p_polygon_vertex[0]);
@ -856,9 +845,9 @@ void FBXMeshData::triangulate_polygon(Ref<SurfaceTool> st, Vector<int> p_polygon
is_simple_convex = true;
Vector3 first_vec;
for (int i = 0; i < polygon_vertex_count; i += 1) {
const Vector3 p1 = p_vertices[p_surface_vertex_map[p_polygon_vertex[i]]];
const Vector3 p2 = p_vertices[p_surface_vertex_map[p_polygon_vertex[(i + 1) % polygon_vertex_count]]];
const Vector3 p3 = p_vertices[p_surface_vertex_map[p_polygon_vertex[(i + 2) % polygon_vertex_count]]];
const Vector3 p1 = p_vertices[surface->vertices_map[p_polygon_vertex[i]]];
const Vector3 p2 = p_vertices[surface->vertices_map[p_polygon_vertex[(i + 1) % polygon_vertex_count]]];
const Vector3 p3 = p_vertices[surface->vertices_map[p_polygon_vertex[(i + 2) % polygon_vertex_count]]];
const Vector3 edge1 = p1 - p2;
const Vector3 edge2 = p3 - p2;
@ -893,7 +882,7 @@ void FBXMeshData::triangulate_polygon(Ref<SurfaceTool> st, Vector<int> p_polygon
std::vector<Vector3> poly_vertices(polygon_vertex_count);
for (int i = 0; i < polygon_vertex_count; i += 1) {
poly_vertices[i] = p_vertices[p_surface_vertex_map[p_polygon_vertex[i]]];
poly_vertices[i] = p_vertices[surface->vertices_map[p_polygon_vertex[i]]];
}
const Vector3 poly_norm = get_poly_normal(poly_vertices);

View file

@ -32,6 +32,8 @@
#define FBX_MESH_DATA_H
#include "core/templates/hash_map.h"
#include "core/templates/local_vector.h"
#include "core/templates/ordered_hash_map.h"
#include "editor/import/resource_importer_scene.h"
#include "editor/import/scene_importer_mesh_node_3d.h"
#include "scene/3d/mesh_instance_3d.h"
@ -47,6 +49,20 @@ struct FBXMeshData;
struct FBXBone;
struct ImportState;
typedef int Vertex;
typedef int SurfaceId;
typedef int PolygonId;
typedef int DataIndex;
struct SurfaceData {
Ref<SurfaceTool> surface_tool;
OrderedHashMap<Vertex, int> lookup_table; // proposed fix is to replace lookup_table[vertex_id] to give the position of the vertices_map[int] index.
LocalVector<Vertex> vertices_map; // this must be ordered the same as insertion <-- slow to do find() operation.
Ref<Material> material;
HashMap<PolygonId, Vector<DataIndex>> surface_polygon_vertex;
Array morphs;
};
struct VertexWeightMapping {
Vector<real_t> weights;
Vector<int> bones;
@ -127,7 +143,7 @@ private:
const Vector3 &p_morph_value = Vector3(),
const Vector3 &p_morph_normal = Vector3());
void triangulate_polygon(Ref<SurfaceTool> st, Vector<int> p_polygon_vertex, Vector<int> p_surface_vertex_map, const std::vector<Vector3> &p_vertices) const;
void triangulate_polygon(SurfaceData *surface, const Vector<int> &p_polygon_vertex, const std::vector<Vector3> &p_vertices) const;
/// This function is responsible to convert the FBX polygon vertex to
/// vertex index.

View file

@ -33,7 +33,7 @@
#include "tools/import_utils.h"
void PivotTransform::ReadTransformChain() {
const FBXDocParser::PropertyTable *props = fbx_model->Props();
const FBXDocParser::PropertyTable *props = fbx_model;
const FBXDocParser::Model::RotOrder &rot = fbx_model->RotationOrder();
const FBXDocParser::TransformInheritance &inheritType = fbx_model->InheritType();
inherit_type = inheritType; // copy the inherit type we need it in the second step.

View file

@ -44,7 +44,6 @@
#include "scene/3d/bone_attachment_3d.h"
#include "scene/3d/camera_3d.h"
#include "scene/3d/light_3d.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/main/node.h"
#include "scene/resources/material.h"
@ -121,15 +120,27 @@ Node3D *EditorSceneImporterFBX::import_scene(const String &p_path, uint32_t p_fl
print_verbose("[doc] opening fbx file: " + p_path);
print_verbose("[doc] fbx header: " + fbx_header_string);
bool corrupt = false;
// safer to check this way as there can be different formatted headers
if (fbx_header_string.find("Kaydara FBX Binary", 0) != -1) {
is_binary = true;
print_verbose("[doc] is binary");
FBXDocParser::TokenizeBinary(tokens, (const char *)data.ptrw(), (size_t)data.size());
FBXDocParser::TokenizeBinary(tokens, (const char *)data.ptrw(), (size_t)data.size(), corrupt);
} else {
print_verbose("[doc] is ascii");
FBXDocParser::Tokenize(tokens, (const char *)data.ptrw(), (size_t)data.size());
FBXDocParser::Tokenize(tokens, (const char *)data.ptrw(), (size_t)data.size(), corrupt);
}
if (corrupt) {
for (FBXDocParser::TokenPtr token : tokens) {
delete token;
}
tokens.clear();
ERR_PRINT(vformat("Cannot import FBX file: %s the file is corrupt so we safely exited parsing the file.", p_path));
return memnew(Node3D);
}
// The import process explained:
@ -141,6 +152,16 @@ Node3D *EditorSceneImporterFBX::import_scene(const String &p_path, uint32_t p_fl
// use this information to construct a very rudimentary
// parse-tree representing the FBX scope structure
FBXDocParser::Parser parser(tokens, is_binary);
if (parser.IsCorrupt()) {
for (FBXDocParser::TokenPtr token : tokens) {
delete token;
}
tokens.clear();
ERR_PRINT(vformat("Cannot import FBX file: %s the file is corrupt so we safely exited parsing the file.", p_path));
return memnew(Node3D);
}
FBXDocParser::ImportSettings settings;
settings.strictMode = false;
@ -153,12 +174,10 @@ Node3D *EditorSceneImporterFBX::import_scene(const String &p_path, uint32_t p_fl
// safety for version handling
if (doc.IsSafeToImport()) {
bool is_blender_fbx = false;
//const FBXDocParser::PropertyPtr app_vendor = p_document->GlobalSettingsPtr()->Props()
// p_document->Creator()
const FBXDocParser::PropertyTable *import_props = doc.GetMetadataProperties();
const FBXDocParser::PropertyPtr app_name = import_props->Get("Original|ApplicationName");
const FBXDocParser::PropertyPtr app_vendor = import_props->Get("Original|ApplicationVendor");
const FBXDocParser::PropertyPtr app_version = import_props->Get("Original|ApplicationVersion");
const FBXDocParser::PropertyTable &import_props = doc.GetMetadataProperties();
const FBXDocParser::PropertyPtr app_name = import_props.Get("Original|ApplicationName");
const FBXDocParser::PropertyPtr app_vendor = import_props.Get("Original|ApplicationVendor");
const FBXDocParser::PropertyPtr app_version = import_props.Get("Original|ApplicationVersion");
//
if (app_name) {
const FBXDocParser::TypedProperty<std::string> *app_name_string = dynamic_cast<const FBXDocParser::TypedProperty<std::string> *>(app_name);
@ -200,6 +219,11 @@ Node3D *EditorSceneImporterFBX::import_scene(const String &p_path, uint32_t p_fl
return spatial;
} else {
for (FBXDocParser::TokenPtr token : tokens) {
delete token;
}
tokens.clear();
ERR_PRINT(vformat("Cannot import FBX file: %s. It uses file format %d which is unsupported by Godot. Please re-export it or convert it to a newer format.", p_path, doc.FBXVersion()));
}
}
@ -892,7 +916,7 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
uint64_t target_id = target->ID();
String target_name = ImportUtils::FBXNodeToName(target->Name());
const FBXDocParser::PropertyTable *properties = curve_node->Props();
const FBXDocParser::PropertyTable *properties = curve_node;
bool got_x = false, got_y = false, got_z = false;
float offset_x = FBXDocParser::PropertyGet<float>(properties, "d|X", got_x);
float offset_y = FBXDocParser::PropertyGet<float>(properties, "d|Y", got_y);
@ -1047,7 +1071,7 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
Ref<FBXNode> target_node = state.fbx_target_map[target_id];
const FBXDocParser::Model *model = target_node->fbx_model;
const FBXDocParser::PropertyTable *props = model->Props();
const FBXDocParser::PropertyTable *props = dynamic_cast<const FBXDocParser::PropertyTable *>(model);
Map<StringName, FBXTrack> &track_data = track->value();
FBXTrack &translation_keys = track_data[StringName("T")];

View file

@ -130,9 +130,7 @@ AnimationCurve::~AnimationCurve() {
AnimationCurveNode::AnimationCurveNode(uint64_t id, const ElementPtr element, const std::string &name,
const Document &doc, const char *const *target_prop_whitelist /*= NULL*/,
size_t whitelist_size /*= 0*/) :
Object(id, element, name), doc(doc) {
const ScopePtr sc = GetRequiredScope(element);
Object(id, element, name), target(), doc(doc) {
// find target node
const char *whitelist[] = { "Model", "NodeAttribute", "Deformer" };
const std::vector<const Connection *> &conns = doc.GetConnectionsBySourceSequenced(ID(), whitelist, 3);
@ -154,8 +152,6 @@ AnimationCurveNode::AnimationCurveNode(uint64_t id, const ElementPtr element, co
prop = con->PropertyName();
break;
}
props = GetPropertyTable(doc, "AnimationCurveNode.FbxAnimCurveNode", element, sc, false);
}
// ------------------------------------------------------------------------------------------------
@ -187,10 +183,6 @@ const AnimationMap &AnimationCurveNode::Curves() const {
// ------------------------------------------------------------------------------------------------
AnimationLayer::AnimationLayer(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) :
Object(id, element, name), doc(doc) {
const ScopePtr sc = GetRequiredScope(element);
// note: the props table here bears little importance and is usually absent
props = GetPropertyTable(doc, "AnimationLayer.FbxAnimLayer", element, sc, true);
}
// ------------------------------------------------------------------------------------------------
@ -248,11 +240,6 @@ const AnimationCurveNodeList AnimationLayer::Nodes(const char *const *target_pro
// ------------------------------------------------------------------------------------------------
AnimationStack::AnimationStack(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) :
Object(id, element, name) {
const ScopePtr sc = GetRequiredScope(element);
// note: we don't currently use any of these properties so we shouldn't bother if it is missing
props = GetPropertyTable(doc, "AnimationStack.FbxAnimStack", element, sc, true);
// resolve attached animation layers
const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "AnimationLayer");
layers.reserve(conns.size());
@ -282,9 +269,5 @@ AnimationStack::AnimationStack(uint64_t id, const ElementPtr element, const std:
// ------------------------------------------------------------------------------------------------
AnimationStack::~AnimationStack() {
if (props != nullptr) {
delete props;
props = nullptr;
}
}
} // namespace FBXDocParser

View file

@ -130,6 +130,7 @@ Token::Token(const char *sbegin, const char *send, TokenType type, size_t offset
line(offset),
column(BINARY_MARKER) {
#ifdef DEBUG_ENABLED
// contents is bad.. :/
contents = std::string(sbegin, static_cast<size_t>(send - sbegin));
#endif
// calc length
@ -232,9 +233,11 @@ unsigned int ReadString(const char *&sbegin_out, const char *&send_out, const ch
}
// ------------------------------------------------------------------------------------------------
void ReadData(const char *&sbegin_out, const char *&send_out, const char *input, const char *&cursor, const char *end) {
void ReadData(const char *&sbegin_out, const char *&send_out, const char *input, const char *&cursor, const char *end, bool &corrupt) {
if (Offset(cursor, end) < 1) {
TokenizeError("cannot ReadData, out of bounds reading length", input, cursor);
corrupt = true;
return;
}
const char type = *cursor;
@ -328,9 +331,7 @@ void ReadData(const char *&sbegin_out, const char *&send_out, const char *input,
}
cursor += comp_len;
break;
}
// string
} // string
case 'S': {
const char *sb, *se;
// 0 characters can legally happen in such strings
@ -338,11 +339,15 @@ void ReadData(const char *&sbegin_out, const char *&send_out, const char *input,
break;
}
default:
corrupt = true; // must exit
TokenizeError("cannot ReadData, unexpected type code: " + std::string(&type, 1), input, cursor);
return;
}
if (cursor > end) {
corrupt = true; // must exit
TokenizeError("cannot ReadData, the remaining size is too small for the data type: " + std::string(&type, 1), input, cursor);
return;
}
// the type code is contained in the returned range
@ -350,7 +355,7 @@ void ReadData(const char *&sbegin_out, const char *&send_out, const char *input,
}
// ------------------------------------------------------------------------------------------------
bool ReadScope(TokenList &output_tokens, const char *input, const char *&cursor, const char *end, bool const is64bits) {
bool ReadScope(TokenList &output_tokens, const char *input, const char *&cursor, const char *end, bool const is64bits, bool &corrupt) {
// the first word contains the offset at which this block ends
const uint64_t end_offset = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end);
@ -364,8 +369,12 @@ bool ReadScope(TokenList &output_tokens, const char *input, const char *&cursor,
if (end_offset > Offset(input, end)) {
TokenizeError("block offset is out of range", input, cursor);
corrupt = true;
return false;
} else if (end_offset < Offset(input, cursor)) {
TokenizeError("block offset is negative out of range", input, cursor);
corrupt = true;
return false;
}
// the second data word contains the number of properties in the scope
@ -375,7 +384,7 @@ bool ReadScope(TokenList &output_tokens, const char *input, const char *&cursor,
const uint64_t prop_length = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end);
// now comes the name of the scope/key
const char *sbeg, *send;
const char *sbeg = nullptr, *send = nullptr;
ReadString(sbeg, send, input, cursor, end);
output_tokens.push_back(new_Token(sbeg, send, TokenType_KEY, Offset(input, cursor)));
@ -383,7 +392,10 @@ bool ReadScope(TokenList &output_tokens, const char *input, const char *&cursor,
// now come the individual properties
const char *begin_cursor = cursor;
for (unsigned int i = 0; i < prop_count; ++i) {
ReadData(sbeg, send, input, cursor, begin_cursor + prop_length);
ReadData(sbeg, send, input, cursor, begin_cursor + prop_length, corrupt);
if (corrupt) {
return false;
}
output_tokens.push_back(new_Token(sbeg, send, TokenType_DATA, Offset(input, cursor)));
@ -394,6 +406,8 @@ bool ReadScope(TokenList &output_tokens, const char *input, const char *&cursor,
if (Offset(begin_cursor, cursor) != prop_length) {
TokenizeError("property length not reached, something is wrong", input, cursor);
corrupt = true;
return false;
}
// at the end of each nested block, there is a NUL record to indicate
@ -410,13 +424,18 @@ bool ReadScope(TokenList &output_tokens, const char *input, const char *&cursor,
// XXX this is vulnerable to stack overflowing ..
while (Offset(input, cursor) < end_offset - sentinel_block_length) {
ReadScope(output_tokens, input, cursor, input + end_offset - sentinel_block_length, is64bits);
ReadScope(output_tokens, input, cursor, input + end_offset - sentinel_block_length, is64bits, corrupt);
if (corrupt) {
return false;
}
}
output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_CLOSE_BRACKET, Offset(input, cursor)));
for (unsigned int i = 0; i < sentinel_block_length; ++i) {
if (cursor[i] != '\0') {
TokenizeError("failed to read nested block sentinel, expected all bytes to be 0", input, cursor);
corrupt = true;
return false;
}
}
cursor += sentinel_block_length;
@ -424,6 +443,8 @@ bool ReadScope(TokenList &output_tokens, const char *input, const char *&cursor,
if (Offset(input, cursor) != end_offset) {
TokenizeError("scope length not reached, something is wrong", input, cursor);
corrupt = true;
return false;
}
return true;
@ -432,7 +453,7 @@ bool ReadScope(TokenList &output_tokens, const char *input, const char *&cursor,
// ------------------------------------------------------------------------------------------------
// TODO: Test FBX Binary files newer than the 7500 version to check if the 64 bits address behaviour is consistent
void TokenizeBinary(TokenList &output_tokens, const char *input, size_t length) {
void TokenizeBinary(TokenList &output_tokens, const char *input, size_t length, bool &corrupt) {
if (length < 0x1b) {
//TokenizeError("file is too short",0);
}
@ -459,7 +480,7 @@ void TokenizeBinary(TokenList &output_tokens, const char *input, size_t length)
const bool is64bits = version >= 7500;
const char *end = input + length;
while (cursor < end) {
if (!ReadScope(output_tokens, input, cursor, input + length, is64bits)) {
if (!ReadScope(output_tokens, input, cursor, input + length, is64bits, corrupt)) {
break;
}
}

View file

@ -89,10 +89,6 @@ using namespace Util;
// ------------------------------------------------------------------------------------------------
Deformer::Deformer(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
Object(id, element, name) {
const ScopePtr sc = GetRequiredScope(element);
const std::string &classname = ParseTokenAsString(GetRequiredToken(element, 2));
props = GetPropertyTable(doc, "Deformer.Fbx" + classname, element, sc, true);
}
// ------------------------------------------------------------------------------------------------
@ -101,10 +97,6 @@ Deformer::~Deformer() {
Constraint::Constraint(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
Object(id, element, name) {
const ScopePtr sc = GetRequiredScope(element);
const std::string &classname = ParseTokenAsString(GetRequiredToken(element, 2));
// used something.fbx as this is a cache name.
props = GetPropertyTable(doc, "Something.Fbx" + classname, element, sc, true);
}
Constraint::~Constraint() {

View file

@ -228,7 +228,7 @@ ObjectPtr LazyObject::LoadObject() {
// ------------------------------------------------------------------------------------------------
Object::Object(uint64_t id, const ElementPtr element, const std::string &name) :
element(element), name(name), id(id) {
PropertyTable(element), element(element), name(name), id(id) {
}
// ------------------------------------------------------------------------------------------------
@ -237,17 +237,13 @@ Object::~Object() {
}
// ------------------------------------------------------------------------------------------------
FileGlobalSettings::FileGlobalSettings(const Document &doc, const PropertyTable *props) :
props(props), doc(doc) {
FileGlobalSettings::FileGlobalSettings(const Document &doc) :
PropertyTable(), doc(doc) {
// empty
}
// ------------------------------------------------------------------------------------------------
FileGlobalSettings::~FileGlobalSettings() {
if (props != nullptr) {
delete props;
props = nullptr;
}
}
// ------------------------------------------------------------------------------------------------
@ -287,15 +283,12 @@ Document::~Document() {
delete v.second;
}
if (metadata_properties != nullptr) {
delete metadata_properties;
}
// clear globals import pointer
globals.reset();
}
// ------------------------------------------------------------------------------------------------
static const unsigned int LowerSupportedVersion = 7300;
static const unsigned int LowerSupportedVersion = 7100;
static const unsigned int UpperSupportedVersion = 7700;
bool Document::ReadHeader() {
@ -306,6 +299,11 @@ bool Document::ReadHeader() {
DOMError("no FBXHeaderExtension dictionary found");
}
if (parser.IsCorrupt()) {
DOMError("File is corrupt");
return false;
}
const ScopePtr shead = ehead->Compound();
fbxVersion = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(shead, "FBXVersion", ehead), 0));
@ -325,18 +323,11 @@ bool Document::ReadHeader() {
creator = ParseTokenAsString(GetRequiredToken(ecreator, 0));
}
//
// Scene Info
//
const ElementPtr scene_info = shead->GetElement("SceneInfo");
if (scene_info) {
PropertyTable *fileExportProps = const_cast<PropertyTable *>(GetPropertyTable(*this, "", scene_info, scene_info->Compound(), true));
if (fileExportProps) {
metadata_properties = fileExportProps;
}
metadata_properties.Setup(scene_info);
}
const ElementPtr etimestamp = shead->GetElement("CreationTimeStamp");
@ -358,23 +349,7 @@ bool Document::ReadHeader() {
void Document::ReadGlobalSettings() {
ERR_FAIL_COND_MSG(globals != nullptr, "Global settings is already setup this is a serious error and should be reported");
const ScopePtr sc = parser.GetRootScope();
const ElementPtr ehead = sc->GetElement("GlobalSettings");
if (nullptr == ehead || !ehead->Compound()) {
DOMWarning("no GlobalSettings dictionary found");
globals = std::make_shared<FileGlobalSettings>(*this, new PropertyTable());
return;
}
const PropertyTable *props = GetPropertyTable(*this, "", ehead, ehead->Compound(), true);
//double v = PropertyGet<float>( *props, std::string("UnitScaleFactor"), 1.0 );
if (!props) {
DOMError("GlobalSettings dictionary contains no property table");
}
globals = std::make_shared<FileGlobalSettings>(*this, props);
globals = std::make_shared<FileGlobalSettings>(*this);
}
// ------------------------------------------------------------------------------------------------
@ -445,58 +420,6 @@ void Document::ReadObjects() {
// ------------------------------------------------------------------------------------------------
void Document::ReadPropertyTemplates() {
const ScopePtr sc = parser.GetRootScope();
// read property templates from "Definitions" section
const ElementPtr edefs = sc->GetElement("Definitions");
if (!edefs || !edefs->Compound()) {
DOMWarning("no Definitions dictionary found");
return;
}
const ScopePtr sdefs = edefs->Compound();
const ElementCollection otypes = sdefs->GetCollection("ObjectType");
for (ElementMap::const_iterator it = otypes.first; it != otypes.second; ++it) {
const ElementPtr el = (*it).second;
const ScopePtr sc_2 = el->Compound();
if (!sc_2) {
DOMWarning("expected nested scope in ObjectType, ignoring", el);
continue;
}
const TokenList &tok = el->Tokens();
if (tok.empty()) {
DOMWarning("expected name for ObjectType element, ignoring", el);
continue;
}
const std::string &oname = ParseTokenAsString(tok[0]);
const ElementCollection templs = sc_2->GetCollection("PropertyTemplate");
for (ElementMap::const_iterator iter = templs.first; iter != templs.second; ++iter) {
const ElementPtr el_2 = (*iter).second;
const ScopePtr sc_3 = el_2->Compound();
if (!sc_3) {
DOMWarning("expected nested scope in PropertyTemplate, ignoring", el);
continue;
}
const TokenList &tok_2 = el_2->Tokens();
if (tok_2.empty()) {
DOMWarning("expected name for PropertyTemplate element, ignoring", el);
continue;
}
const std::string &pname = ParseTokenAsString(tok_2[0]);
const ElementPtr Properties70 = sc_3->GetElement("Properties70");
if (Properties70) {
// PropertyTable(const ElementPtr element, const PropertyTable* templateProps);
const PropertyTable *props = new PropertyTable(Properties70, nullptr);
templates[oname + "." + pname] = props;
}
}
}
}
// ------------------------------------------------------------------------------------------------

View file

@ -130,7 +130,7 @@ private:
};
/** Base class for in-memory (DOM) representations of FBX objects */
class Object {
class Object : public PropertyTable {
public:
Object(uint64_t id, const ElementPtr element, const std::string &name);
@ -149,9 +149,9 @@ public:
}
protected:
const ElementPtr element;
const ElementPtr element = nullptr;
const std::string name;
const uint64_t id = 0;
const uint64_t id;
};
/** DOM class for generic FBX NoteAttribute blocks. NoteAttribute's just hold a property table,
@ -159,22 +159,13 @@ protected:
class NodeAttribute : public Object {
public:
NodeAttribute(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
virtual ~NodeAttribute();
const PropertyTable *Props() const {
return props;
}
private:
const PropertyTable *props;
};
/** DOM base class for FBX camera settings attached to a node */
class CameraSwitcher : public NodeAttribute {
public:
CameraSwitcher(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
virtual ~CameraSwitcher();
int CameraID() const {
@ -190,26 +181,26 @@ public:
}
private:
int cameraId;
int cameraId = 0;
std::string cameraName;
std::string cameraIndexName;
};
#define fbx_stringize(a) #a
#define fbx_simple_property(name, type, default_value) \
type name() const { \
return PropertyGet<type>(Props(), fbx_stringize(name), (default_value)); \
#define fbx_simple_property(name, type, default_value) \
type name() const { \
return PropertyGet<type>(this, fbx_stringize(name), (default_value)); \
}
// XXX improve logging
#define fbx_simple_enum_property(name, type, default_value) \
type name() const { \
const int ival = PropertyGet<int>(Props(), fbx_stringize(name), static_cast<int>(default_value)); \
if (ival < 0 || ival >= AI_CONCAT(type, _MAX)) { \
return static_cast<type>(default_value); \
} \
return static_cast<type>(ival); \
#define fbx_simple_enum_property(name, type, default_value) \
type name() const { \
const int ival = PropertyGet<int>(this, fbx_stringize(name), static_cast<int>(default_value)); \
if (ival < 0 || ival >= AI_CONCAT(type, _MAX)) { \
return static_cast<type>(default_value); \
} \
return static_cast<type>(ival); \
}
class FbxPoseNode;
@ -256,7 +247,7 @@ public:
}
private:
uint64_t target_id;
uint64_t target_id = 0;
Transform transform;
};
@ -264,7 +255,6 @@ private:
class Camera : public NodeAttribute {
public:
Camera(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
virtual ~Camera();
fbx_simple_property(Position, Vector3, Vector3(0, 0, 0));
@ -380,7 +370,6 @@ public:
};
Model(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
virtual ~Model();
fbx_simple_property(QuaternionInterpolate, int, 0);
@ -466,10 +455,6 @@ public:
return culling;
}
const PropertyTable *Props() const {
return props;
}
/** Get material links */
const std::vector<const Material *> &GetMaterials() const {
return materials;
@ -498,13 +483,11 @@ private:
std::string shading;
std::string culling;
const PropertyTable *props = nullptr;
};
class ModelLimbNode : public Model {
public:
ModelLimbNode(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
virtual ~ModelLimbNode();
};
@ -512,7 +495,6 @@ public:
class Texture : public Object {
public:
Texture(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
virtual ~Texture();
const std::string &Type() const {
@ -539,10 +521,6 @@ public:
return uvScaling;
}
const PropertyTable *Props() const {
return props;
}
// return a 4-tuple
const unsigned int *Crop() const {
return crop;
@ -560,10 +538,8 @@ private:
std::string relativeFileName;
std::string fileName;
std::string alphaSource;
const PropertyTable *props = nullptr;
unsigned int crop[4] = { 0 };
const Video *media = nullptr;
};
@ -626,8 +602,8 @@ public:
private:
std::vector<const Texture *> textures;
BlendMode blendMode;
float alpha;
BlendMode blendMode = BlendMode::BlendMode_Additive;
float alpha = 0;
};
typedef std::map<std::string, const Texture *> TextureMap;
@ -656,10 +632,6 @@ public:
return relativeFileName;
}
const PropertyTable *Props() const {
return props;
}
const uint8_t *Content() const {
return content;
}
@ -687,7 +659,6 @@ private:
std::string type;
std::string relativeFileName;
std::string fileName;
const PropertyTable *props = nullptr;
uint64_t contentLength = 0;
uint8_t *content = nullptr;
@ -708,10 +679,6 @@ public:
return multilayer;
}
const PropertyTable *Props() const {
return props;
}
const TextureMap &Textures() const {
return textures;
}
@ -722,8 +689,7 @@ public:
private:
std::string shading;
bool multilayer;
const PropertyTable *props;
bool multilayer = false;
TextureMap textures;
LayeredTextureMap layeredTextures;
@ -791,10 +757,6 @@ public:
virtual ~AnimationCurveNode();
const PropertyTable *Props() const {
return props;
}
const AnimationMap &Curves() const;
/** Object the curve is assigned to, this can be NULL if the
@ -819,7 +781,6 @@ public:
private:
Object *target = nullptr;
const PropertyTable *props;
mutable AnimationMap curves;
std::string prop;
const Document &doc;
@ -837,18 +798,12 @@ public:
AnimationLayer(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc);
virtual ~AnimationLayer();
const PropertyTable *Props() const {
//ai_assert(props.get());
return props;
}
/* the optional white list specifies a list of property names for which the caller
wants animations for. Curves not matching this list will not be added to the
animation layer. */
const AnimationCurveNodeList Nodes(const char *const *target_prop_whitelist = nullptr, size_t whitelist_size = 0) const;
private:
const PropertyTable *props;
const Document &doc;
};
@ -863,16 +818,11 @@ public:
fbx_simple_property(ReferenceStart, int64_t, 0L);
fbx_simple_property(ReferenceStop, int64_t, 0L);
const PropertyTable *Props() const {
return props;
}
const AnimationLayerList &Layers() const {
return layers;
}
private:
const PropertyTable *props = nullptr;
AnimationLayerList layers;
};
@ -881,14 +831,6 @@ class Deformer : public Object {
public:
Deformer(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
virtual ~Deformer();
const PropertyTable *Props() const {
//ai_assert(props.get());
return props;
}
private:
const PropertyTable *props;
};
/** Constraints are from Maya they can help us with BoneAttachments :) **/
@ -896,9 +838,6 @@ class Constraint : public Object {
public:
Constraint(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
virtual ~Constraint();
private:
const PropertyTable *props;
};
typedef std::vector<float> WeightArray;
@ -924,7 +863,7 @@ public:
}
private:
float percent;
float percent = 0;
WeightArray fullWeights;
std::vector<const ShapeGeometry *> shapeGeometries;
};
@ -1006,7 +945,7 @@ private:
Transform transformLink;
Transform transformAssociateModel;
SkinLinkMode link_mode;
bool valid_transformAssociateModel;
bool valid_transformAssociateModel = false;
const Model *node = nullptr;
};
@ -1037,8 +976,8 @@ public:
}
private:
float accuracy;
SkinType skinType;
float accuracy = 0;
SkinType skinType = SkinType::Skin_Linear;
std::vector<const Cluster *> clusters;
};
@ -1087,10 +1026,10 @@ public:
}
public:
uint64_t insertionOrder;
uint64_t insertionOrder = 0;
const std::string prop;
uint64_t src, dest;
uint64_t src = 0, dest = 0;
const Document &doc;
};
@ -1105,15 +1044,10 @@ typedef std::multimap<uint64_t, const Connection *> ConnectionMap;
/** DOM class for global document settings, a single instance per document can
* be accessed via Document.Globals(). */
class FileGlobalSettings {
class FileGlobalSettings : public PropertyTable {
public:
FileGlobalSettings(const Document &doc, const PropertyTable *props);
~FileGlobalSettings();
const PropertyTable *Props() const {
return props;
}
FileGlobalSettings(const Document &doc);
virtual ~FileGlobalSettings();
const Document &GetDocument() const {
return doc;
@ -1158,7 +1092,6 @@ public:
fbx_simple_property(CustomFrameRate, float, -1.0f);
private:
const PropertyTable *props = nullptr;
const Document &doc;
};
@ -1196,7 +1129,7 @@ public:
return globals.get();
}
const PropertyTable *GetMetadataProperties() const {
const PropertyTable &GetMetadataProperties() const {
return metadata_properties;
}
@ -1293,7 +1226,7 @@ private:
std::vector<uint64_t> materials;
std::vector<uint64_t> skins;
mutable std::vector<const AnimationStack *> animationStacksResolved;
PropertyTable *metadata_properties = nullptr;
PropertyTable metadata_properties;
std::shared_ptr<FileGlobalSettings> globals = nullptr;
};
} // namespace FBXDocParser

View file

@ -137,36 +137,5 @@ void DOMWarning(const std::string &message, const std::shared_ptr<Element> eleme
print_verbose("[FBX-DOM] warning:" + String(message.c_str()));
}
// ------------------------------------------------------------------------------------------------
// fetch a property table and the corresponding property template
const PropertyTable *GetPropertyTable(const Document &doc,
const std::string &templateName,
const ElementPtr element,
const ScopePtr sc,
bool no_warn /*= false*/) {
// todo: make this an abstraction
const ElementPtr Properties70 = sc->GetElement("Properties70");
const PropertyTable *templateProps = static_cast<const PropertyTable *>(nullptr);
if (templateName.length()) {
PropertyTemplateMap::const_iterator it = doc.Templates().find(templateName);
if (it != doc.Templates().end()) {
templateProps = (*it).second;
}
}
if (!Properties70 || !Properties70->Compound()) {
if (!no_warn) {
DOMWarning("property table (Properties70) not found", element);
}
if (templateProps) {
return new const PropertyTable(templateProps);
} else {
return new const PropertyTable();
}
}
return new PropertyTable(Properties70, templateProps);
}
} // namespace Util
} // namespace FBXDocParser

View file

@ -98,13 +98,6 @@ void DOMWarning(const std::string &message, const Element *element);
void DOMWarning(const std::string &message, const std::shared_ptr<Token> token);
void DOMWarning(const std::string &message, const std::shared_ptr<Element> element);
// fetch a property table and the corresponding property template
const PropertyTable *GetPropertyTable(const Document &doc,
const std::string &templateName,
const ElementPtr element,
const ScopePtr sc,
bool no_warn = false);
// ------------------------------------------------------------------------------------------------
template <typename T>
const T *ProcessSimpleConnection(const Connection &con,

View file

@ -118,8 +118,6 @@ Material::Material(uint64_t id, const ElementPtr element, const Document &doc, c
DOMWarning("shading mode not recognized: " + shading, element);
}
props = GetPropertyTable(doc, templateName, element, sc);
// resolve texture links
const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID());
for (const Connection *con : conns) {
@ -163,10 +161,6 @@ Material::Material(uint64_t id, const ElementPtr element, const Document &doc, c
// ------------------------------------------------------------------------------------------------
Material::~Material() {
if (props != nullptr) {
delete props;
props = nullptr;
}
}
// ------------------------------------------------------------------------------------------------
@ -219,17 +213,15 @@ Texture::Texture(uint64_t id, const ElementPtr element, const Document &doc, con
alphaSource = ParseTokenAsString(GetRequiredToken(Texture_Alpha_Source, 0));
}
props = GetPropertyTable(doc, "Texture.FbxFileTexture", element, sc);
// 3DS Max and FBX SDK use "Scaling" and "Translation" instead of "ModelUVScaling" and "ModelUVTranslation". Use these properties if available.
bool ok;
const Vector3 &scaling = PropertyGet<Vector3>(props, "Scaling", ok);
bool ok = true;
const Vector3 &scaling = PropertyGet<Vector3>(this, "Scaling", ok);
if (ok) {
uvScaling.x = scaling.x;
uvScaling.y = scaling.y;
}
const Vector3 &trans = PropertyGet<Vector3>(props, "Translation", ok);
const Vector3 &trans = PropertyGet<Vector3>(this, "Translation", ok);
if (ok) {
uvTrans.x = trans.x;
uvTrans.y = trans.y;
@ -254,10 +246,6 @@ Texture::Texture(uint64_t id, const ElementPtr element, const Document &doc, con
}
Texture::~Texture() {
if (props != nullptr) {
delete props;
props = nullptr;
}
}
LayeredTexture::LayeredTexture(uint64_t id, const ElementPtr element, const Document & /*doc*/, const std::string &name) :
@ -390,18 +378,11 @@ Video::Video(uint64_t id, const ElementPtr element, const Document &doc, const s
// runtimeError.what());
}
}
props = GetPropertyTable(doc, "Video.FbxVideo", element, sc);
}
Video::~Video() {
if (content) {
delete[] content;
}
if (props != nullptr) {
delete props;
props = nullptr;
}
}
} // namespace FBXDocParser

View file

@ -98,16 +98,11 @@ Model::Model(uint64_t id, const ElementPtr element, const Document &doc, const s
culling = ParseTokenAsString(GetRequiredToken(Culling, 0));
}
props = GetPropertyTable(doc, "Model.FbxNode", element, sc);
ResolveLinks(element, doc);
}
// ------------------------------------------------------------------------------------------------
Model::~Model() {
if (props != nullptr) {
delete props;
props = nullptr;
}
}
ModelLimbNode::ModelLimbNode(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :

View file

@ -84,16 +84,7 @@ using namespace Util;
// ------------------------------------------------------------------------------------------------
NodeAttribute::NodeAttribute(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
Object(id, element, name), props() {
const ScopePtr sc = GetRequiredScope(element);
const std::string &classname = ParseTokenAsString(GetRequiredToken(element, 2));
// hack on the deriving type but Null/LimbNode attributes are the only case in which
// the property table is by design absent and no warning should be generated
// for it.
const bool is_null_or_limb = !strcmp(classname.c_str(), "Null") || !strcmp(classname.c_str(), "LimbNode");
props = GetPropertyTable(doc, "NodeAttribute.Fbx" + classname, element, sc, is_null_or_limb);
Object(id, element, name) {
}
// ------------------------------------------------------------------------------------------------

View file

@ -131,6 +131,8 @@ Element::Element(const TokenPtr key_token, Parser &parser) :
if (!n) {
print_error("unexpected end of file, expected bracket, comma or key" + String(parser.LastToken()->StringContents().c_str()));
parser.corrupt = true;
return;
}
const TokenType ty = n->Type();
@ -143,6 +145,8 @@ Element::Element(const TokenPtr key_token, Parser &parser) :
if (ty != TokenType_OPEN_BRACKET && ty != TokenType_CLOSE_BRACKET && ty != TokenType_COMMA && ty != TokenType_KEY) {
print_error("unexpected token; expected bracket, comma or key" + String(n->StringContents().c_str()));
parser.corrupt = true;
return;
}
}
@ -150,11 +154,17 @@ Element::Element(const TokenPtr key_token, Parser &parser) :
compound = new_Scope(parser);
parser.scopes.push_back(compound);
if (parser.corrupt) {
return;
}
// current token should be a TOK_CLOSE_BRACKET
n = parser.CurrentToken();
if (n && n->Type() != TokenType_CLOSE_BRACKET) {
print_error("expected closing bracket" + String(n->StringContents().c_str()));
parser.corrupt = true;
return;
}
parser.AdvanceToNextToken();
@ -173,22 +183,31 @@ Scope::Scope(Parser &parser, bool topLevel) {
TokenPtr t = parser.CurrentToken();
if (t->Type() != TokenType_OPEN_BRACKET) {
print_error("expected open bracket" + String(t->StringContents().c_str()));
parser.corrupt = true;
return;
}
}
TokenPtr n = parser.AdvanceToNextToken();
if (n == nullptr) {
print_error("unexpected end of file");
parser.corrupt = true;
return;
}
// note: empty scopes are allowed
while (n && n->Type() != TokenType_CLOSE_BRACKET) {
if (n->Type() != TokenType_KEY) {
print_error("unexpected token, expected TOK_KEY" + String(n->StringContents().c_str()));
parser.corrupt = true;
return;
}
const std::string str = n->StringContents();
if (parser.corrupt) {
return;
}
// std::multimap<std::string, ElementPtr> (key and value)
elements.insert(ElementMap::value_type(str, new_Element(n, parser)));
@ -216,7 +235,7 @@ Scope::~Scope() {
// ------------------------------------------------------------------------------------------------
Parser::Parser(const TokenList &tokens, bool is_binary) :
tokens(tokens), cursor(tokens.begin()), is_binary(is_binary) {
corrupt(false), tokens(tokens), cursor(tokens.begin()), is_binary(is_binary) {
root = new_Scope(*this, true);
scopes.push_back(root);
}
@ -1230,6 +1249,21 @@ ScopePtr GetRequiredScope(const ElementPtr el) {
ERR_FAIL_V_MSG(nullptr, "Invalid element supplied to parser");
}
// ------------------------------------------------------------------------------------------------
// extract optional compound scope
ScopePtr GetOptionalScope(const ElementPtr el) {
if (el) {
ScopePtr s = el->Compound();
TokenPtr token = el->KeyToken();
if (token && s) {
return s;
}
}
return nullptr;
}
// ------------------------------------------------------------------------------------------------
// get token at a particular index
TokenPtr GetRequiredToken(const ElementPtr el, unsigned int index) {

View file

@ -199,6 +199,10 @@ public:
return is_binary;
}
bool IsCorrupt() const {
return corrupt;
}
private:
friend class Scope;
friend class Element;
@ -208,6 +212,7 @@ private:
TokenPtr CurrentToken() const;
private:
bool corrupt = false;
ScopeList scopes;
const TokenList &tokens;
@ -249,6 +254,8 @@ bool HasElement(const ScopePtr sc, const std::string &index);
// extract a required element from a scope, abort if the element cannot be found
ElementPtr GetRequiredElement(const ScopePtr sc, const std::string &index, const ElementPtr element = nullptr);
ScopePtr GetRequiredScope(const ElementPtr el); // New in 2020. (less likely to destroy application)
ScopePtr GetOptionalScope(const ElementPtr el); // New in 2021. (even LESS likely to destroy application now)
ElementPtr GetOptionalElement(const ScopePtr sc, const std::string &index, const ElementPtr element = nullptr);
// extract required compound scope
ScopePtr GetRequiredScope(const ElementPtr el);

View file

@ -145,19 +145,33 @@ std::string PeekPropertyName(const Element &element) {
} // namespace
// ------------------------------------------------------------------------------------------------
PropertyTable::PropertyTable() {
PropertyTable::PropertyTable() :
element(nullptr) {
}
// Is used when dealing with FBX Objects not metadata.
PropertyTable::PropertyTable(const ElementPtr element) :
element(element) {
Setup(element);
}
// ------------------------------------------------------------------------------------------------
PropertyTable::PropertyTable(const PropertyTable *templateProps) :
templateProps(templateProps), element() {
PropertyTable::~PropertyTable() {
for (PropertyMap::value_type &v : props) {
delete v.second;
}
}
// ------------------------------------------------------------------------------------------------
PropertyTable::PropertyTable(const ElementPtr element, const PropertyTable *templateProps) :
templateProps(templateProps), element(element) {
const ScopePtr scope = GetRequiredScope(element);
ERR_FAIL_COND(!scope);
void PropertyTable::Setup(ElementPtr ptr) {
const ScopePtr sc = GetRequiredScope(ptr);
const ElementPtr Properties70 = sc->GetElement("Properties70");
const ScopePtr scope = GetOptionalScope(Properties70);
// no scope, no care.
if (!scope) {
return; // NOTE: this is not an error this is actually a Object, without properties, here we will nullptr it.
}
for (const ElementMap::value_type &v : scope->Elements()) {
if (v.first != "P") {
DOMWarning("expected only P elements in property table", v.second);
@ -181,13 +195,6 @@ PropertyTable::PropertyTable(const ElementPtr element, const PropertyTable *temp
}
}
// ------------------------------------------------------------------------------------------------
PropertyTable::~PropertyTable() {
for (PropertyMap::value_type &v : props) {
delete v.second;
}
}
// ------------------------------------------------------------------------------------------------
PropertyPtr PropertyTable::Get(const std::string &name) const {
PropertyMap::const_iterator it = props.find(name);
@ -203,10 +210,6 @@ PropertyPtr PropertyTable::Get(const std::string &name) const {
if (it == props.end()) {
// check property template
if (templateProps) {
return templateProps->Get(name);
}
return nullptr;
}
}

View file

@ -137,36 +137,31 @@ class PropertyTable {
public:
// in-memory property table with no source element
PropertyTable();
PropertyTable(const PropertyTable *templateProps);
PropertyTable(const ElementPtr element, const PropertyTable *templateProps);
~PropertyTable();
PropertyTable(const ElementPtr element);
virtual ~PropertyTable();
PropertyPtr Get(const std::string &name) const;
void Setup(ElementPtr ptr);
// PropertyTable's need not be coupled with FBX elements so this can be NULL
ElementPtr GetElement() const {
ElementPtr GetElement() {
return element;
}
PropertyMap &GetProperties() const {
PropertyMap &GetProperties() {
return props;
}
const LazyPropertyMap &GetLazyProperties() const {
const LazyPropertyMap &GetLazyProperties() {
return lazyProps;
}
const PropertyTable *TemplateProps() const {
return templateProps;
}
DirectPropertyMap GetUnparsedProperties() const;
private:
LazyPropertyMap lazyProps;
mutable PropertyMap props;
const PropertyTable *templateProps = nullptr;
const ElementPtr element = nullptr;
ElementPtr element = nullptr;
};
// ------------------------------------------------------------------------------------------------
@ -191,16 +186,11 @@ template <typename T>
inline T PropertyGet(const PropertyTable *in, const std::string &name, bool &result, bool useTemplate = false) {
PropertyPtr prop = in->Get(name);
if (nullptr == prop) {
if (!useTemplate) {
if (nullptr == in) {
result = false;
return T();
}
const PropertyTable *templ = in->TemplateProps();
if (nullptr == templ) {
result = false;
return T();
}
prop = templ->Get(name);
prop = in->Get(name);
if (nullptr == prop) {
result = false;
return T();

View file

@ -141,7 +141,7 @@ void ProcessDataToken(TokenList &output_tokens, const char *&start, const char *
} // namespace
// ------------------------------------------------------------------------------------------------
void Tokenize(TokenList &output_tokens, const char *input, size_t length) {
void Tokenize(TokenList &output_tokens, const char *input, size_t length, bool &corrupt) {
// line and column numbers numbers are one-based
unsigned int line = 1;
unsigned int column = 1;
@ -185,6 +185,8 @@ void Tokenize(TokenList &output_tokens, const char *input, size_t length) {
case '\"':
if (token_begin) {
TokenizeError("unexpected double-quote", line, column);
corrupt = true;
return;
}
token_begin = cur;
in_double_quotes = true;

View file

@ -187,7 +187,7 @@ typedef std::vector<TokenPtr> TokenList;
* @param output_tokens Receives a list of all tokens in the input data.
* @param input_buffer Textual input buffer to be processed, 0-terminated.
* @print_error if something goes wrong */
void Tokenize(TokenList &output_tokens, const char *input, size_t length);
void Tokenize(TokenList &output_tokens, const char *input, size_t length, bool &corrupt);
/** Tokenizer function for binary FBX files.
*
@ -197,7 +197,7 @@ void Tokenize(TokenList &output_tokens, const char *input, size_t length);
* @param input_buffer Binary input buffer to be processed.
* @param length Length of input buffer, in bytes. There is no 0-terminal.
* @print_error if something goes wrong */
void TokenizeBinary(TokenList &output_tokens, const char *input, size_t length);
void TokenizeBinary(TokenList &output_tokens, const char *input, size_t length, bool &corrupt);
} // namespace FBXDocParser
#endif // FBX_TOKENIZER_H