Explorar el Código

Merge pull request #47880 from RevoluPowered/fix-fbx-parser

FBX Improve Parser and File Compatibility
Rémi Verschelde hace 4 años
padre
commit
88015f4e72

+ 2 - 2
modules/fbx/data/fbx_material.cpp

@@ -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()));

+ 12 - 23
modules/fbx/data/fbx_mesh_data.cpp

@@ -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);

+ 17 - 1
modules/fbx/data/fbx_mesh_data.h

@@ -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.

+ 1 - 1
modules/fbx/data/pivot_transform.cpp

@@ -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.

+ 35 - 11
modules/fbx/editor_scene_importer_fbx.cpp

@@ -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")];

+ 1 - 18
modules/fbx/fbx_parser/FBXAnimation.cpp

@@ -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

+ 31 - 10
modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp

@@ -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;
 		}
 	}

+ 0 - 8
modules/fbx/fbx_parser/FBXDeformer.cpp

@@ -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() {

+ 11 - 88
modules/fbx/fbx_parser/FBXDocument.cpp

@@ -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;
-			}
-		}
-	}
 }
 
 // ------------------------------------------------------------------------------------------------

+ 29 - 96
modules/fbx/fbx_parser/FBXDocument.h

@@ -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

+ 0 - 31
modules/fbx/fbx_parser/FBXDocumentUtil.cpp

@@ -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

+ 0 - 7
modules/fbx/fbx_parser/FBXDocumentUtil.h

@@ -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,

+ 3 - 22
modules/fbx/fbx_parser/FBXMaterial.cpp

@@ -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

+ 0 - 5
modules/fbx/fbx_parser/FBXModel.cpp

@@ -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) :

+ 1 - 10
modules/fbx/fbx_parser/FBXNodeAttribute.cpp

@@ -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) {
 }
 
 // ------------------------------------------------------------------------------------------------

+ 35 - 1
modules/fbx/fbx_parser/FBXParser.cpp

@@ -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) {

+ 7 - 0
modules/fbx/fbx_parser/FBXParser.h

@@ -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);

+ 22 - 19
modules/fbx/fbx_parser/FBXProperties.cpp

@@ -145,19 +145,33 @@ std::string PeekPropertyName(const Element &element) {
 } // namespace
 
 // ------------------------------------------------------------------------------------------------
-PropertyTable::PropertyTable() {
+PropertyTable::PropertyTable() :
+		element(nullptr) {
 }
 
-// ------------------------------------------------------------------------------------------------
-PropertyTable::PropertyTable(const PropertyTable *templateProps) :
-		templateProps(templateProps), element() {
+// Is used when dealing with FBX Objects not metadata.
+PropertyTable::PropertyTable(const ElementPtr element) :
+		element(element) {
+	Setup(element);
 }
 
 // ------------------------------------------------------------------------------------------------
-PropertyTable::PropertyTable(const ElementPtr element, const PropertyTable *templateProps) :
-		templateProps(templateProps), element(element) {
-	const ScopePtr scope = GetRequiredScope(element);
-	ERR_FAIL_COND(!scope);
+PropertyTable::~PropertyTable() {
+	for (PropertyMap::value_type &v : props) {
+		delete v.second;
+	}
+}
+
+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;
 		}
 	}

+ 9 - 19
modules/fbx/fbx_parser/FBXProperties.h

@@ -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) {
-			result = false;
-			return T();
-		}
-		const PropertyTable *templ = in->TemplateProps();
-		if (nullptr == templ) {
+		if (nullptr == in) {
 			result = false;
 			return T();
 		}
-		prop = templ->Get(name);
+		prop = in->Get(name);
 		if (nullptr == prop) {
 			result = false;
 			return T();

+ 3 - 1
modules/fbx/fbx_parser/FBXTokenizer.cpp

@@ -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;

+ 2 - 2
modules/fbx/fbx_parser/FBXTokenizer.h

@@ -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