mirror of
https://github.com/godotengine/godot.git
synced 2024-11-26 22:23:04 +00:00
Merge pull request #47880 from RevoluPowered/fix-fbx-parser
FBX Improve Parser and File Compatibility
This commit is contained in:
commit
88015f4e72
@ -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