mirror of
https://github.com/godotengine/godot.git
synced 2024-11-22 20:23:53 +00:00
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:
parent
a86e7c3bb7
commit
061b77e5e6
@ -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()));
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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")];
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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) :
|
||||
|
@ -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) {
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user