diff options
Diffstat (limited to 'modules/fbx')
52 files changed, 0 insertions, 14504 deletions
diff --git a/modules/fbx/README.md b/modules/fbx/README.md deleted file mode 100644 index 8eca4bd3c9..0000000000 --- a/modules/fbx/README.md +++ /dev/null @@ -1,196 +0,0 @@ -# Open Source FBX Specification for the Importer - -The goal of this document is to make everything in FBX clearly stated, any errors will be corrected over time this -is a first draft. - -## fbx parser - originally from assimp - -- Folder: /modules/fbx/fbx_parser -- Upstream: assimp -- Original Version: git (308db73d0b3c2d1870cd3e465eaa283692a4cf23, 2019) -- License: BSD-3-Clause - -This can never be updated from upstream, we have heavily modified the parser to provide memory safety and add some -functionality. If anything we should give this parser back to assimp at some point as it has a lot of new features. - -# Updating assimp fbx parser - -Don't. It's not possible the code is rewritten in many areas to remove thirdparty deps and various bugs are fixed. - -Many days were put into rewriting the parser to use safe code and safe memory accessors. - -# File Headers - -FBX Binaries start with the header "Kaydara FBX Binary" - -FBX ASCII documents contain a larger header, sometimes with copyright information for a file. - -Detecting these is pretty simple. - -# What is an OP link? -It's an object to property link. It lists the properties for that object in some cases. Source and destination based by -ID. - -# What is a OO link? -Its an object to object link, it contains the ID source and destination ID. - -# FBX Node connections - -Nodes in FBX are connected using OO links, This means Object to Object. - -FBX has a single other kind of link which is Object Property, this is used for Object to Property Links, this can be - extra attributes, defaults, or even some simple settings. - -# Bones / Joints / Locators - -Bones in FBX are nodes, they initially have the Model:: Type, then have links to SubDeformer the sub deformer -is part of the skin there is also an explicit Skin link, which then links to the geometry using OO links in the -document. - -# Rotation Order in FBX compared to Godot - -**Godot uses the rotation order:** YXZ - -**FBX has dynamic rotation order to prevent gimbal lock with complex animations** - -```cpp -enum RotOrder { - RotOrder_EulerXYZ = 0 - RotOrder_EulerXZY, - RotOrder_EulerYZX, - RotOrder_EulerYXZ, - RotOrder_EulerZXY, - RotOrder_EulerZYX, - RotOrder_SphericXYZ // nobody uses this - as far as we can tell -}; -``` - - -# Pivot transforms - -### Pivot description: -- Maya and 3DS max consider everything to be in node space (bones joints, skins, lights, cameras, etc) -- Everything is a node, this means essentially nodes are auto or variants -- They are local to the node in the tree. -- They are used to calculate where a node is in space -```c++ -// For a better reference you can check editor_scene_importer_fbx.h -// references: GenFBXTransform / read the data in -// references: ComputePivotTransform / run the calculation -// This is the local pivot transform for the node, not the global transforms -Transform ComputePivotTransform( - Transform3D chain[TransformationComp_MAXIMUM], - Transform3D &geometric_transform) { - // Maya pivots - Transform3D T = chain[TransformationComp_Translation]; - Transform3D Roff = chain[TransformationComp_RotationOffset]; - Transform3D Rp = chain[TransformationComp_RotationPivot]; - Transform3D Rpre = chain[TransformationComp_PreRotation]; - Transform3D R = chain[TransformationComp_Rotation]; - Transform3D Rpost = chain[TransformationComp_PostRotation]; - Transform3D Soff = chain[TransformationComp_ScalingOffset]; - Transform3D Sp = chain[TransformationComp_ScalingPivot]; - Transform3D S = chain[TransformationComp_Scaling]; - - // 3DS Max Pivots - Transform3D OT = chain[TransformationComp_GeometricTranslation]; - Transform3D OR = chain[TransformationComp_GeometricRotation]; - Transform3D OS = chain[TransformationComp_GeometricScaling]; - - // Calculate 3DS max pivot transform - use geometric space (e.g doesn't effect children nodes only the current node) - geometric_transform = OT * OR * OS; - // Calculate standard maya pivots - return T * Roff * Rp * Rpre * R * Rpost.inverse() * Rp.inverse() * Soff * Sp * S * Sp.inverse(); -} -``` - -# Transform inheritance for FBX Nodes - -The goal of below is to explain why they implement this in the first place. - -The use case is to make nodes have an option to override their local scaling or to make scaling influenced by orientation, which i would imagine would be useful for when you need to rotate a node and the child to scale based on the orientation rather than setting on the rotation matrix planes. -```cpp -// not modified the formatting here since this code must remain clear -enum TransformInheritance { - Transform_RrSs = 0, - // Parent Rotation * Local Rotation * Parent Scale * Local Scale -- Parent Rotation Offset * Parent ScalingOffset (Local scaling is offset by rotation of parent node) - Transform_RSrs = 1, // Parent Rotation * Parent Scale * Local Rotation * Local Scale -- Parent * Local (normal mode) - Transform_Rrs = 2, // Parent Rotation * Local Rotation * Local Scale -- Node transform scale is the only relevant component - TransformInheritance_MAX // end-of-enum sentinel -}; - -enum TransformInheritance { - Transform_RrSs = 0, - // Local scaling is offset by rotation of parent node - Transform_RSrs = 1, - // Parent * Local (normal mode) - Transform_Rrs = 2, - // Node transform scale is the only relevant component - TransformInheritance_MAX // end-of-enum sentinel -}; -``` - -# Axis in FBX - -Godot has one format for the declared axis - -AXIS X, AXIS Y, -AXIS Z - -FBX supports any format you can think of. As it has to support Maya and 3DS Max. - -#### FBX File Header -```json -GlobalSettings: { - Version: 1000 - Properties70: { - P: "UpAxis", "int", "Integer", "",1 - P: "UpAxisSign", "int", "Integer", "",1 - P: "FrontAxis", "int", "Integer", "",2 - P: "FrontAxisSign", "int", "Integer", "",1 - P: "CoordAxis", "int", "Integer", "",0 - P: "CoordAxisSign", "int", "Integer", "",1 - P: "OriginalUpAxis", "int", "Integer", "",1 - P: "OriginalUpAxisSign", "int", "Integer", "",1 - P: "UnitScaleFactor", "double", "Number", "",1 - P: "OriginalUnitScaleFactor", "double", "Number", "",1 - P: "AmbientColor", "ColorRGB", "Color", "",0,0,0 - P: "DefaultCamera", "KString", "", "", "Producer Perspective" - P: "TimeMode", "enum", "", "",6 - P: "TimeProtocol", "enum", "", "",2 - P: "SnapOnFrameMode", "enum", "", "",0 - P: "TimeSpanStart", "KTime", "Time", "",0 - P: "TimeSpanStop", "KTime", "Time", "",92372316000 - P: "CustomFrameRate", "double", "Number", "",-1 - P: "TimeMarker", "Compound", "", "" - P: "CurrentTimeMarker", "int", "Integer", "",-1 - } -} -``` - -#### FBX FILE declares axis dynamically using FBX header -Coord is X -Up is Y -Front is Z - -#### GODOT - constant reference point -Coord is X positive, -Y is up positive, -Front is -Z negative - -### Explaining MeshGeometry indexing - -Reference type declared: -- Direct (directly related to the mapping information type) -- IndexToDirect (Map with key value, meaning depends on the MappingInformationType) - -ControlPoint is a vertex -* None The mapping is undetermined. -* ByVertex There will be one mapping coordinate for each surface control point/vertex. - * If you have direct reference type vertices [x] - * If you have IndexToDirect reference type the UV -* ByPolygonVertex There will be one mapping coordinate for each vertex, for every polygon of which it is a part. This means that a vertex will have as many mapping coordinates as polygons of which it is a part. (Sorted by polygon, referencing vertex) -* ByPolygon There can be only one mapping coordinate for the whole polygon. - * One mapping per polygon polygon x has this normal x - * For each vertex of the polygon then set the normal to x -* ByEdge There will be one mapping coordinate for each unique edge in the mesh. This is meant to be used with smoothing layer elements. (Mapping is referencing the edge id) -* AllSame There can be only one mapping coordinate for the whole surface. diff --git a/modules/fbx/SCsub b/modules/fbx/SCsub deleted file mode 100644 index 0311fddfee..0000000000 --- a/modules/fbx/SCsub +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python - -Import("env") -Import("env_modules") - -env_fbx = env_modules.Clone() - -# Make includes relative to the folder path specified here so our includes are clean -env_fbx.Prepend(CPPPATH=["#modules/fbx/"]) - -if env["builtin_zlib"]: - env_fbx.Prepend(CPPPATH=["#thirdparty/zlib/"]) - -# Godot's own source files -env_fbx.add_source_files(env.modules_sources, "tools/*.cpp") -env_fbx.add_source_files(env.modules_sources, "data/*.cpp") -env_fbx.add_source_files(env.modules_sources, "fbx_parser/*.cpp") -env_fbx.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/fbx/config.py b/modules/fbx/config.py deleted file mode 100644 index 78929800b3..0000000000 --- a/modules/fbx/config.py +++ /dev/null @@ -1,16 +0,0 @@ -def can_build(env, platform): - return env["tools"] - - -def configure(env): - pass - - -def get_doc_classes(): - return [ - "EditorSceneImporterFBX", - ] - - -def get_doc_path(): - return "doc_classes" diff --git a/modules/fbx/data/fbx_anim_container.h b/modules/fbx/data/fbx_anim_container.h deleted file mode 100644 index 8c25d65871..0000000000 --- a/modules/fbx/data/fbx_anim_container.h +++ /dev/null @@ -1,46 +0,0 @@ -/*************************************************************************/ -/* fbx_anim_container.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef FBX_ANIM_CONTAINER_H -#define FBX_ANIM_CONTAINER_H - -#include "core/math/vector3.h" - -// Generic keyframes 99.99 percent of files will be vector3, except if quat interp is used, or visibility tracks -// FBXTrack is used in a map in the implementation in fbx/editor_scene_importer_fbx.cpp -// to avoid having to rewrite the entire logic I refactored this into the code instead. -// once it works I can rewrite so we can add the fun misc features / small features -struct FBXTrack { - bool has_default = false; - Vector3 default_value; - std::map<int64_t, Vector3> keyframes; -}; - -#endif //MODEL_ABSTRACTION_ANIM_CONTAINER_H diff --git a/modules/fbx/data/fbx_bone.cpp b/modules/fbx/data/fbx_bone.cpp deleted file mode 100644 index 38dada33af..0000000000 --- a/modules/fbx/data/fbx_bone.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/*************************************************************************/ -/* fbx_bone.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "fbx_bone.h" - -#include "fbx_node.h" -#include "import_state.h" - -Ref<FBXNode> FBXSkinDeformer::get_link(const ImportState &state) const { - print_verbose("bone name: " + bone->bone_name); - - // safe for when deformers must be polyfilled when skin has different count of binds to bones in the scene ;) - if (!cluster) { - return nullptr; - } - - ERR_FAIL_COND_V_MSG(cluster->TargetNode() == nullptr, nullptr, "bone has invalid target node"); - - Ref<FBXNode> link_node; - uint64_t id = cluster->TargetNode()->ID(); - if (state.fbx_target_map.has(id)) { - link_node = state.fbx_target_map[id]; - } else { - print_error("link node not found for " + itos(id)); - } - - // the node in space this is for, like if it's FOR a target. - return link_node; -} diff --git a/modules/fbx/data/fbx_bone.h b/modules/fbx/data/fbx_bone.h deleted file mode 100644 index 83918ad1e2..0000000000 --- a/modules/fbx/data/fbx_bone.h +++ /dev/null @@ -1,90 +0,0 @@ -/*************************************************************************/ -/* fbx_bone.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef FBX_BONE_H -#define FBX_BONE_H - -#include "fbx_node.h" -#include "import_state.h" - -#include "fbx_parser/FBXDocument.h" - -struct PivotTransform; - -struct FBXBone : public RefCounted { - uint64_t parent_bone_id = 0; - uint64_t bone_id = 0; - - bool valid_parent = false; // if the parent bone id is set up. - String bone_name = String(); // bone name - - bool is_root_bone() const { - return !valid_parent; - } - - // Godot specific data - int godot_bone_id = -2; // godot internal bone id assigned after import - - // if a bone / armature is the root then FBX skeleton will contain the bone not any other skeleton. - // this is to support joints by themselves in scenes - bool valid_armature_id = false; - uint64_t armature_id = 0; - - /* link node is the parent bone */ - mutable const FBXDocParser::Geometry *geometry = nullptr; - mutable const FBXDocParser::ModelLimbNode *limb_node = nullptr; - - void set_node(Ref<FBXNode> p_node) { - node = p_node; - } - - // Stores the pivot xform for this bone - - Ref<FBXNode> node = nullptr; - Ref<FBXBone> parent_bone = nullptr; - Ref<FBXSkeleton> fbx_skeleton = nullptr; -}; - -struct FBXSkinDeformer { - FBXSkinDeformer(Ref<FBXBone> p_bone, const FBXDocParser::Cluster *p_cluster) : - cluster(p_cluster), bone(p_bone) {} - ~FBXSkinDeformer() {} - const FBXDocParser::Cluster *cluster; - Ref<FBXBone> bone; - - /* get associate model - the model can be invalid sometimes */ - Ref<FBXBone> get_associate_model() const { - return bone->parent_bone; - } - - Ref<FBXNode> get_link(const ImportState &state) const; -}; - -#endif // FBX_BONE_H diff --git a/modules/fbx/data/fbx_material.cpp b/modules/fbx/data/fbx_material.cpp deleted file mode 100644 index 86baec4244..0000000000 --- a/modules/fbx/data/fbx_material.cpp +++ /dev/null @@ -1,468 +0,0 @@ -/*************************************************************************/ -/* fbx_material.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "fbx_material.h" - -// FIXME: Shouldn't depend on core_bind.h! Use DirAccessRef like the rest of -// the engine instead of core_bind::Directory. -#include "core/core_bind.h" -#include "scene/resources/material.h" -#include "scene/resources/texture.h" -#include "tools/validation_tools.h" - -String FBXMaterial::get_material_name() const { - return material_name; -} - -void FBXMaterial::set_imported_material(FBXDocParser::Material *p_material) { - material = p_material; -} - -void FBXMaterial::add_search_string(String p_filename, String p_current_directory, String search_directory, Vector<String> &texture_search_paths) { - if (search_directory.is_empty()) { - texture_search_paths.push_back(p_current_directory.get_base_dir().plus_file(p_filename)); - } else { - texture_search_paths.push_back(p_current_directory.get_base_dir().plus_file(search_directory + "/" + p_filename)); - texture_search_paths.push_back(p_current_directory.get_base_dir().plus_file("../" + search_directory + "/" + p_filename)); - } -} - -String find_file(const String &p_base, const String &p_file_to_find) { - core_bind::Directory dir; - dir.open(p_base); - - dir.list_dir_begin(); - String n = dir.get_next(); - while (n != String()) { - if (n == "." || n == "..") { - n = dir.get_next(); - continue; - } - if (dir.current_is_dir()) { - // Don't use `path_to` or the returned path will be wrong. - const String f = find_file(p_base + "/" + n, p_file_to_find); - if (f != "") { - return f; - } - } else if (n == p_file_to_find) { - return p_base + "/" + n; - } - n = dir.get_next(); - } - dir.list_dir_end(); - - return String(); -} - -// fbx will not give us good path information and let's not regex them to fix them -// no relative paths are in fbx generally they have a rel field but it's populated incorrectly by the SDK. -String FBXMaterial::find_texture_path_by_filename(const String p_filename, const String p_current_directory) { - core_bind::Directory dir; - Vector<String> paths; - add_search_string(p_filename, p_current_directory, "", paths); - add_search_string(p_filename, p_current_directory, "texture", paths); - add_search_string(p_filename, p_current_directory, "textures", paths); - add_search_string(p_filename, p_current_directory, "Textures", paths); - add_search_string(p_filename, p_current_directory, "materials", paths); - add_search_string(p_filename, p_current_directory, "mats", paths); - add_search_string(p_filename, p_current_directory, "pictures", paths); - add_search_string(p_filename, p_current_directory, "images", paths); - - for (int i = 0; i < paths.size(); i++) { - if (dir.file_exists(paths[i])) { - return paths[i]; - } - } - - // We were not able to find the texture in the common locations, - // try to find it into the project globally. - // The common textures can be stored into one of those folders: - // res://asset - // res://texture - // res://material - // res://mat - // res://image - // res://picture - // - // Note the folders can also be called with custom names, like: - // res://my_assets - // since the keyword `asset` is into the directory name the textures will be - // searched there too. - - dir.open("res://"); - dir.list_dir_begin(); - String n = dir.get_next(); - while (n != String()) { - if (n == "." || n == "..") { - n = dir.get_next(); - continue; - } - if (dir.current_is_dir()) { - const String lower_n = n.to_lower(); - if ( - // Don't need to use plural. - lower_n.find("asset") >= 0 || - lower_n.find("texture") >= 0 || - lower_n.find("material") >= 0 || - lower_n.find("mat") >= 0 || - lower_n.find("image") >= 0 || - lower_n.find("picture") >= 0) { - // Don't use `path_to` or the returned path will be wrong. - const String f = find_file(String("res://") + n, p_filename); - if (f != "") { - return f; - } - } - } - n = dir.get_next(); - } - dir.list_dir_end(); - - return ""; -} - -template <class T> -T extract_from_prop(FBXDocParser::PropertyPtr prop, const T &p_default, const std::string &p_name, const String &p_type) { - ERR_FAIL_COND_V_MSG(prop == nullptr, p_default, "invalid property passed to extractor"); - const FBXDocParser::TypedProperty<T> *val = dynamic_cast<const FBXDocParser::TypedProperty<T> *>(prop); - - ERR_FAIL_COND_V_MSG(val == nullptr, p_default, "The FBX is corrupted, the property `" + String(p_name.c_str()) + "` is a `" + String(typeid(*prop).name()) + "` but should be a " + p_type); - // Make sure to not lost any eventual opacity. - return val->Value(); -} - -Ref<StandardMaterial3D> FBXMaterial::import_material(ImportState &state) { - ERR_FAIL_COND_V(material == nullptr, nullptr); - - const String p_fbx_current_directory = state.path; - - Ref<StandardMaterial3D> spatial_material; - spatial_material.instantiate(); - - // read the material file - // is material two sided - // read material name - print_verbose("[material] material name: " + ImportUtils::FBXNodeToName(material->Name())); - - material_name = ImportUtils::FBXNodeToName(material->Name()); - - for (const std::pair<std::string, const FBXDocParser::Texture *> iter : material->Textures()) { - const uint64_t texture_id = iter.second->ID(); - const std::string &fbx_mapping_name = iter.first; - const FBXDocParser::Texture *fbx_texture_data = iter.second; - const String absolute_texture_path = iter.second->FileName().c_str(); - const String texture_name = absolute_texture_path.get_file(); - const String file_extension = absolute_texture_path.get_extension().to_upper(); - - const String debug_string = "texture id: " + itos(texture_id) + " texture name: " + String(iter.second->Name().c_str()) + " mapping name: " + String(fbx_mapping_name.c_str()); - // remember errors STILL need this string at the end for when you aren't in verbose debug mode :) they need context for when you're not verbose-ing. - print_verbose(debug_string); - - const String file_extension_uppercase = file_extension.to_upper(); - - if (fbx_transparency_flags.count(fbx_mapping_name) > 0) { - // just enable it later let's make this fine-tuned. - spatial_material->set_transparency(BaseMaterial3D::TRANSPARENCY_ALPHA); - } - - ERR_CONTINUE_MSG(file_extension.is_empty(), "your texture has no file extension so we had to ignore it, let us know if you think this is wrong file an issue on github! " + debug_string); - ERR_CONTINUE_MSG(fbx_texture_map.count(fbx_mapping_name) <= 0, "This material has a texture with mapping name: " + String(fbx_mapping_name.c_str()) + " which is not yet supported by this importer. Consider opening an issue so we can support it."); - ERR_CONTINUE_MSG( - file_extension_uppercase != "PNG" && - file_extension_uppercase != "JPEG" && - file_extension_uppercase != "JPG" && - file_extension_uppercase != "TGA" && - file_extension_uppercase != "WEBP" && - file_extension_uppercase != "DDS", - "The FBX file contains a texture with an unrecognized extension: " + file_extension_uppercase); - - print_verbose("Getting FBX mapping mode for " + String(fbx_mapping_name.c_str())); - // get the texture map type - const StandardMaterial3D::TextureParam mapping_mode = fbx_texture_map.at(fbx_mapping_name); - print_verbose("Set FBX mapping mode to " + get_texture_param_name(mapping_mode)); - - Ref<Texture> texture; - print_verbose("texture mapping name: " + texture_name); - - if (state.cached_image_searches.has(texture_name)) { - texture = state.cached_image_searches[texture_name]; - } else { - String path = find_texture_path_by_filename(texture_name, p_fbx_current_directory); - if (!path.is_empty()) { - Ref<Texture2D> image_texture = ResourceLoader::load(path); - - ERR_CONTINUE(image_texture.is_null()); - - texture = image_texture; - state.cached_image_searches.insert(texture_name, texture); - print_verbose("Created texture from loaded image file."); - - } else if (fbx_texture_data != nullptr && fbx_texture_data->Media() != nullptr && fbx_texture_data->Media()->IsEmbedded()) { - // This is an embedded texture. Extract it. - Ref<Image> image; - //image.instantiate(); // oooo double instance bug? why make Image::_png_blah call - - const String extension = texture_name.get_extension().to_upper(); - if (extension == "PNG") { - // The stored file is a PNG. - image = Image::_png_mem_loader_func(fbx_texture_data->Media()->Content(), fbx_texture_data->Media()->ContentLength()); - ERR_CONTINUE_MSG(image.is_valid() == false, "FBX Embedded PNG image load fail."); - - } else if ( - extension == "JPEG" || - extension == "JPG") { - // The stored file is a JPEG. - image = Image::_jpg_mem_loader_func(fbx_texture_data->Media()->Content(), fbx_texture_data->Media()->ContentLength()); - ERR_CONTINUE_MSG(image.is_valid() == false, "FBX Embedded JPEG image load fail."); - - } else if (extension == "TGA") { - // The stored file is a TGA. - image = Image::_tga_mem_loader_func(fbx_texture_data->Media()->Content(), fbx_texture_data->Media()->ContentLength()); - ERR_CONTINUE_MSG(image.is_valid() == false, "FBX Embedded TGA image load fail."); - - } else if (extension == "WEBP") { - // The stored file is a WEBP. - image = Image::_webp_mem_loader_func(fbx_texture_data->Media()->Content(), fbx_texture_data->Media()->ContentLength()); - ERR_CONTINUE_MSG(image.is_valid() == false, "FBX Embedded WEBP image load fail."); - - // } else if (extension == "DDS") { - // // In this moment is not possible to extract a DDS from a buffer, TODO consider add it to godot. See `textureloader_dds.cpp::load(). - // // The stored file is a DDS. - } else { - ERR_CONTINUE_MSG(true, "The embedded image with extension: " + extension + " is not yet supported. Open an issue please."); - } - - Ref<ImageTexture> image_texture; - image_texture.instantiate(); - image_texture->create_from_image(image); - - texture = image_texture; - - // TODO: this is potentially making something with the same name have a match incorrectly USE FBX ID as Hash. #fuck it later. - state.cached_image_searches[texture_name] = texture; - print_verbose("Created texture from embedded image."); - } else { - ERR_CONTINUE_MSG(true, "The FBX texture, with name: `" + texture_name + "`, is not found into the project nor is stored as embedded file. Make sure to insert the texture as embedded file or into the project, then reimport."); - } - } - - spatial_material->set_texture(mapping_mode, texture); - } - - if (spatial_material.is_valid()) { - spatial_material->set_name(material_name); - } - - /// ALL below is related to properties - for (FBXDocParser::LazyPropertyMap::value_type iter : material->GetLazyProperties()) { - const std::string name = iter.first; - - if (name.empty()) { - continue; - } - - PropertyDesc desc = PROPERTY_DESC_NOT_FOUND; - if (fbx_properties_desc.count(name) > 0) { - desc = fbx_properties_desc.at(name); - } - - // check if we can ignore this it will be done at the next phase - if (desc == PROPERTY_DESC_NOT_FOUND || desc == PROPERTY_DESC_IGNORE) { - // count the texture mapping references. Skip this one if it's found and we can't look up a property value. - if (fbx_texture_map.count(name) > 0) { - continue; // safe to ignore it's a texture mapping. - } - } - - if (desc == PROPERTY_DESC_IGNORE) { - //WARN_PRINT("[Ignored] The FBX material parameter: `" + String(name.c_str()) + "` is ignored."); - continue; - } else { - print_verbose("FBX Material parameter: " + String(name.c_str())); - - // Check for Diffuse material system / lambert materials / legacy basically - if (name == "Diffuse" && !warning_non_pbr_material) { - ValidationTracker::get_singleton()->add_validation_error(state.path, "Invalid material settings change to Ai Standard Surface shader, mat name: " + material_name.c_escape()); - warning_non_pbr_material = true; - } - } - - // DISABLE when adding support for all weird and wonderful material formats - if (desc == PROPERTY_DESC_NOT_FOUND) { - continue; - } - - 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; - 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())); - - if (spatial_material.is_null()) { - // Done here so if no data no material is created. - spatial_material.instantiate(); - } - - const FBXDocParser::TypedProperty<real_t> *real_value = dynamic_cast<const FBXDocParser::TypedProperty<real_t> *>(prop); - const FBXDocParser::TypedProperty<Vector3> *vector_value = dynamic_cast<const FBXDocParser::TypedProperty<Vector3> *>(prop); - - if (!real_value && !vector_value) { - //WARN_PRINT("unsupported datatype in property: " + String(name.c_str())); - continue; - } - - if (vector_value && !real_value) { - if (vector_value->Value() == Vector3(0, 0, 0) && !real_value) { - continue; - } - } - - switch (desc) { - case PROPERTY_DESC_ALBEDO_COLOR: { - if (vector_value) { - const Vector3 &color = vector_value->Value(); - // Make sure to not lost any eventual opacity. - if (color != Vector3(0, 0, 0)) { - Color c = Color(); - c[0] = color[0]; - c[1] = color[1]; - c[2] = color[2]; - spatial_material->set_albedo(c); - } - - } else if (real_value) { - print_error("albedo is unsupported format?"); - } - } break; - case PROPERTY_DESC_TRANSPARENT: { - if (real_value) { - const real_t opacity = real_value->Value(); - if (opacity < (1.0 - CMP_EPSILON)) { - Color c = spatial_material->get_albedo(); - c.a = opacity; - spatial_material->set_albedo(c); - - spatial_material->set_transparency(BaseMaterial3D::TRANSPARENCY_ALPHA); - spatial_material->set_depth_draw_mode(BaseMaterial3D::DEPTH_DRAW_OPAQUE_ONLY); - } - } else if (vector_value) { - print_error("unsupported transparent desc type vector!"); - } - } break; - case PROPERTY_DESC_SPECULAR: { - if (real_value) { - print_verbose("specular real value: " + rtos(real_value->Value())); - spatial_material->set_specular(MIN(1.0, real_value->Value())); - } - - if (vector_value) { - print_error("unsupported specular vector value: " + vector_value->Value()); - } - } break; - - case PROPERTY_DESC_SPECULAR_COLOR: { - if (vector_value) { - print_error("unsupported specular color: " + vector_value->Value()); - } - } break; - case PROPERTY_DESC_SHINYNESS: { - if (real_value) { - print_error("unsupported shinyness:" + rtos(real_value->Value())); - } - } break; - case PROPERTY_DESC_METALLIC: { - if (real_value) { - print_verbose("metallic real value: " + rtos(real_value->Value())); - spatial_material->set_metallic(MIN(1.0f, real_value->Value())); - } else { - print_error("unsupported value type for metallic"); - } - } break; - case PROPERTY_DESC_ROUGHNESS: { - if (real_value) { - print_verbose("roughness real value: " + rtos(real_value->Value())); - spatial_material->set_roughness(MIN(1.0f, real_value->Value())); - } else { - print_error("unsupported value type for roughness"); - } - } break; - case PROPERTY_DESC_COAT: { - if (real_value) { - print_verbose("clearcoat real value: " + rtos(real_value->Value())); - spatial_material->set_clearcoat(MIN(1.0f, real_value->Value())); - } else { - print_error("unsupported value type for clearcoat"); - } - } break; - case PROPERTY_DESC_COAT_ROUGHNESS: { - // meaning is that approx equal to zero is disabled not actually zero. ;) - if (real_value && Math::is_zero_approx(real_value->Value())) { - print_verbose("clearcoat real value: " + rtos(real_value->Value())); - spatial_material->set_clearcoat_gloss(1.0 - real_value->Value()); - } else { - print_error("unsupported value type for clearcoat gloss"); - } - } break; - case PROPERTY_DESC_EMISSIVE: { - if (real_value && Math::is_zero_approx(real_value->Value())) { - print_verbose("Emissive real value: " + rtos(real_value->Value())); - spatial_material->set_emission_energy(real_value->Value()); - } else if (vector_value && !vector_value->Value().is_equal_approx(Vector3(0, 0, 0))) { - const Vector3 &color = vector_value->Value(); - Color c; - c[0] = color[0]; - c[1] = color[1]; - c[2] = color[2]; - spatial_material->set_emission(c); - } - } break; - case PROPERTY_DESC_EMISSIVE_COLOR: { - if (vector_value && !vector_value->Value().is_equal_approx(Vector3(0, 0, 0))) { - const Vector3 &color = vector_value->Value(); - Color c; - c[0] = color[0]; - c[1] = color[1]; - c[2] = color[2]; - spatial_material->set_emission(c); - } else { - print_error("unsupported value type for emissive color"); - } - } break; - case PROPERTY_DESC_NOT_FOUND: - case PROPERTY_DESC_IGNORE: - break; - default: - break; - } - } - - return spatial_material; -} diff --git a/modules/fbx/data/fbx_material.h b/modules/fbx/data/fbx_material.h deleted file mode 100644 index 5fd4d9212b..0000000000 --- a/modules/fbx/data/fbx_material.h +++ /dev/null @@ -1,285 +0,0 @@ -/*************************************************************************/ -/* fbx_material.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef FBX_MATERIAL_H -#define FBX_MATERIAL_H - -#include "tools/import_utils.h" - -#include "core/object/ref_counted.h" -#include "core/string/ustring.h" - -struct FBXMaterial : public RefCounted { - String material_name = String(); - bool warning_non_pbr_material = false; - FBXDocParser::Material *material = nullptr; - - /* Godot materials - *** Texture Maps: - * Albedo - color, texture - * Metallic - specular, metallic, texture - * Roughness - roughness, texture - * Emission - color, texture - * Normal Map - scale, texture - * Ambient Occlusion - texture - * Refraction - scale, texture - *** Has Settings for: - * UV1 - SCALE, OFFSET - * UV2 - SCALE, OFFSET - *** Flags for - * Transparent - * Cull Mode - */ - - enum class MapMode { - AlbedoM = 0, - MetallicM, - SpecularM, - EmissionM, - RoughnessM, - NormalM, - AmbientOcclusionM, - RefractionM, - ReflectionM, - }; - - /* Returns the string representation of the TextureParam enum */ - static String get_texture_param_name(StandardMaterial3D::TextureParam param) { - switch (param) { - case StandardMaterial3D::TEXTURE_ALBEDO: - return "TEXTURE_ALBEDO"; - case StandardMaterial3D::TEXTURE_METALLIC: - return "TEXTURE_METALLIC"; - case StandardMaterial3D::TEXTURE_ROUGHNESS: - return "TEXTURE_ROUGHNESS"; - case StandardMaterial3D::TEXTURE_EMISSION: - return "TEXTURE_EMISSION"; - case StandardMaterial3D::TEXTURE_NORMAL: - return "TEXTURE_NORMAL"; - case StandardMaterial3D::TEXTURE_RIM: - return "TEXTURE_RIM"; - case StandardMaterial3D::TEXTURE_CLEARCOAT: - return "TEXTURE_CLEARCOAT"; - case StandardMaterial3D::TEXTURE_FLOWMAP: - return "TEXTURE_FLOWMAP"; - case StandardMaterial3D::TEXTURE_AMBIENT_OCCLUSION: - return "TEXTURE_AMBIENT_OCCLUSION"; - // case StandardMaterial3D::TEXTURE_DEPTH: // TODO: work out how to make this function again! - // return "TEXTURE_DEPTH"; - case StandardMaterial3D::TEXTURE_SUBSURFACE_SCATTERING: - return "TEXTURE_SUBSURFACE_SCATTERING"; - // case StandardMaterial3D::TEXTURE_TRANSMISSION: // TODO: work out how to make this function again! - // return "TEXTURE_TRANSMISSION"; - case StandardMaterial3D::TEXTURE_REFRACTION: - return "TEXTURE_REFRACTION"; - case StandardMaterial3D::TEXTURE_DETAIL_MASK: - return "TEXTURE_DETAIL_MASK"; - case StandardMaterial3D::TEXTURE_DETAIL_ALBEDO: - return "TEXTURE_DETAIL_ALBEDO"; - case StandardMaterial3D::TEXTURE_DETAIL_NORMAL: - return "TEXTURE_DETAIL_NORMAL"; - case StandardMaterial3D::TEXTURE_MAX: - return "TEXTURE_MAX"; - default: - return "broken horribly"; - } - }; - - // TODO make this static? - const std::map<std::string, bool> fbx_transparency_flags = { - /* Transparent */ - { "TransparentColor", true }, - { "Maya|opacity", true } - }; - - // TODO make this static? - const std::map<std::string, StandardMaterial3D::TextureParam> fbx_texture_map = { - /* Diffuse */ - { "Maya|base", StandardMaterial3D::TextureParam::TEXTURE_ALBEDO }, - { "DiffuseColor", StandardMaterial3D::TextureParam::TEXTURE_ALBEDO }, - { "Maya|DiffuseTexture", StandardMaterial3D::TextureParam::TEXTURE_ALBEDO }, - { "Maya|baseColor", StandardMaterial3D::TextureParam::TEXTURE_ALBEDO }, - { "Maya|baseColor|file", StandardMaterial3D::TextureParam::TEXTURE_ALBEDO }, - { "3dsMax|Parameters|base_color_map", StandardMaterial3D::TextureParam::TEXTURE_ALBEDO }, - { "Maya|TEX_color_map|file", StandardMaterial3D::TextureParam::TEXTURE_ALBEDO }, - { "Maya|TEX_color_map", StandardMaterial3D::TextureParam::TEXTURE_ALBEDO }, - /* Emission */ - { "EmissiveColor", StandardMaterial3D::TextureParam::TEXTURE_EMISSION }, - { "EmissiveFactor", StandardMaterial3D::TextureParam::TEXTURE_EMISSION }, - { "Maya|emissionColor", StandardMaterial3D::TextureParam::TEXTURE_EMISSION }, - { "Maya|emissionColor|file", StandardMaterial3D::TextureParam::TEXTURE_EMISSION }, - { "3dsMax|Parameters|emission_map", StandardMaterial3D::TextureParam::TEXTURE_EMISSION }, - { "Maya|TEX_emissive_map", StandardMaterial3D::TextureParam::TEXTURE_EMISSION }, - { "Maya|TEX_emissive_map|file", StandardMaterial3D::TextureParam::TEXTURE_EMISSION }, - /* Metallic */ - { "Maya|metalness", StandardMaterial3D::TextureParam::TEXTURE_METALLIC }, - { "Maya|metalness|file", StandardMaterial3D::TextureParam::TEXTURE_METALLIC }, - { "3dsMax|Parameters|metalness_map", StandardMaterial3D::TextureParam::TEXTURE_METALLIC }, - { "Maya|TEX_metallic_map", StandardMaterial3D::TextureParam::TEXTURE_METALLIC }, - { "Maya|TEX_metallic_map|file", StandardMaterial3D::TextureParam::TEXTURE_METALLIC }, - - /* Roughness */ - // Arnold Roughness Map - { "Maya|specularRoughness", StandardMaterial3D::TextureParam::TEXTURE_ROUGHNESS }, - - { "3dsMax|Parameters|roughness_map", StandardMaterial3D::TextureParam::TEXTURE_ROUGHNESS }, - { "Maya|TEX_roughness_map", StandardMaterial3D::TextureParam::TEXTURE_ROUGHNESS }, - { "Maya|TEX_roughness_map|file", StandardMaterial3D::TextureParam::TEXTURE_ROUGHNESS }, - - /* Normal */ - { "NormalMap", StandardMaterial3D::TextureParam::TEXTURE_NORMAL }, - //{ "Bump", Material::TextureParam::TEXTURE_NORMAL }, - //{ "3dsMax|Parameters|bump_map", Material::TextureParam::TEXTURE_NORMAL }, - { "Maya|NormalTexture", StandardMaterial3D::TextureParam::TEXTURE_NORMAL }, - //{ "Maya|normalCamera", Material::TextureParam::TEXTURE_NORMAL }, - //{ "Maya|normalCamera|file", Material::TextureParam::TEXTURE_NORMAL }, - { "Maya|TEX_normal_map", StandardMaterial3D::TextureParam::TEXTURE_NORMAL }, - { "Maya|TEX_normal_map|file", StandardMaterial3D::TextureParam::TEXTURE_NORMAL }, - /* AO */ - { "Maya|TEX_ao_map", StandardMaterial3D::TextureParam::TEXTURE_AMBIENT_OCCLUSION }, - { "Maya|TEX_ao_map|file", StandardMaterial3D::TextureParam::TEXTURE_AMBIENT_OCCLUSION }, - - // TODO: specular workflow conversion - // { "SpecularColor", StandardMaterial3D::TextureParam::TEXTURE_METALLIC }, - // { "Maya|specularColor", StandardMaterial3D::TextureParam::TEXTURE_METALLIC }, - // { "Maya|SpecularTexture", StandardMaterial3D::TextureParam::TEXTURE_METALLIC }, - // { "Maya|SpecularTexture|file", StandardMaterial3D::TextureParam::TEXTURE_METALLIC }, - // { "ShininessExponent", SpatialMaterial::TextureParam::UNSUPPORTED }, - // { "ReflectionFactor", SpatialMaterial::TextureParam::UNSUPPORTED }, - - //{ "TransparentColor",SpatialMaterial::TextureParam::TEXTURE_CHANNEL_ALPHA }, - //{ "TransparencyFactor",SpatialMaterial::TextureParam::TEXTURE_CHANNEL_ALPHA } - - // TODO: diffuse roughness - //{ "Maya|diffuseRoughness", SpatialMaterial::TextureParam::UNSUPPORTED }, - //{ "Maya|diffuseRoughness|file", SpatialMaterial::TextureParam::UNSUPPORTED }, - - }; - - // TODO make this static? - enum PropertyDesc { - PROPERTY_DESC_NOT_FOUND, - PROPERTY_DESC_ALBEDO_COLOR, - PROPERTY_DESC_TRANSPARENT, - PROPERTY_DESC_METALLIC, - PROPERTY_DESC_ROUGHNESS, - PROPERTY_DESC_SPECULAR, - PROPERTY_DESC_SPECULAR_COLOR, - PROPERTY_DESC_SHINYNESS, - PROPERTY_DESC_COAT, - PROPERTY_DESC_COAT_ROUGHNESS, - PROPERTY_DESC_EMISSIVE, - PROPERTY_DESC_EMISSIVE_COLOR, - PROPERTY_DESC_IGNORE - }; - - const std::map<std::string, PropertyDesc> fbx_properties_desc = { - /* Albedo */ - { "DiffuseColor", PROPERTY_DESC_ALBEDO_COLOR }, - { "Maya|baseColor", PROPERTY_DESC_ALBEDO_COLOR }, - - /* Specular */ - { "Maya|specular", PROPERTY_DESC_SPECULAR }, - { "Maya|specularColor", PROPERTY_DESC_SPECULAR_COLOR }, - - /* Specular roughness - arnold roughness map */ - { "Maya|specularRoughness", PROPERTY_DESC_ROUGHNESS }, - - /* Transparent */ - { "Opacity", PROPERTY_DESC_TRANSPARENT }, - { "TransparencyFactor", PROPERTY_DESC_TRANSPARENT }, - { "Maya|opacity", PROPERTY_DESC_TRANSPARENT }, - - { "Maya|metalness", PROPERTY_DESC_METALLIC }, - { "Maya|metallic", PROPERTY_DESC_METALLIC }, - - /* Roughness */ - { "Maya|roughness", PROPERTY_DESC_ROUGHNESS }, - - /* Coat */ - //{ "Maya|coat", PROPERTY_DESC_COAT }, - - /* Coat roughness */ - //{ "Maya|coatRoughness", PROPERTY_DESC_COAT_ROUGHNESS }, - - /* Emissive */ - { "Maya|emission", PROPERTY_DESC_EMISSIVE }, - { "Maya|emissive", PROPERTY_DESC_EMISSIVE }, - - /* Emissive color */ - { "EmissiveColor", PROPERTY_DESC_EMISSIVE_COLOR }, - { "Maya|emissionColor", PROPERTY_DESC_EMISSIVE_COLOR }, - - /* Ignore */ - { "Shininess", PROPERTY_DESC_IGNORE }, - { "Reflectivity", PROPERTY_DESC_IGNORE }, - { "Maya|diffuseRoughness", PROPERTY_DESC_IGNORE }, - { "Maya", PROPERTY_DESC_IGNORE }, - { "Diffuse", PROPERTY_DESC_ALBEDO_COLOR }, - { "Maya|TypeId", PROPERTY_DESC_IGNORE }, - { "Ambient", PROPERTY_DESC_IGNORE }, - { "AmbientColor", PROPERTY_DESC_IGNORE }, - { "ShininessExponent", PROPERTY_DESC_IGNORE }, - { "Specular", PROPERTY_DESC_IGNORE }, - { "SpecularColor", PROPERTY_DESC_IGNORE }, - { "SpecularFactor", PROPERTY_DESC_IGNORE }, - //{ "BumpFactor", PROPERTY_DESC_IGNORE }, - { "Maya|exitToBackground", PROPERTY_DESC_IGNORE }, - { "Maya|indirectDiffuse", PROPERTY_DESC_IGNORE }, - { "Maya|indirectSpecular", PROPERTY_DESC_IGNORE }, - { "Maya|internalReflections", PROPERTY_DESC_IGNORE }, - { "DiffuseFactor", PROPERTY_DESC_IGNORE }, - { "AmbientFactor", PROPERTY_DESC_IGNORE }, - { "ReflectionColor", PROPERTY_DESC_IGNORE }, - { "Emissive", PROPERTY_DESC_IGNORE }, - { "Maya|coatColor", PROPERTY_DESC_IGNORE }, - { "Maya|coatNormal", PROPERTY_DESC_IGNORE }, - { "Maya|coatIOR", PROPERTY_DESC_IGNORE }, - }; - - /* storing the texture properties like color */ - template <class T> - struct TexturePropertyMapping : RefCounted { - StandardMaterial3D::TextureParam map_mode = StandardMaterial3D::TextureParam::TEXTURE_ALBEDO; - const T property = T(); - }; - - static void add_search_string(String p_filename, String p_current_directory, String search_directory, Vector<String> &texture_search_paths); - - static String find_texture_path_by_filename(const String p_filename, const String p_current_directory); - - String get_material_name() const; - - void set_imported_material(FBXDocParser::Material *p_material); - - Ref<StandardMaterial3D> import_material(ImportState &state); -}; - -#endif // FBX_MATERIAL_H diff --git a/modules/fbx/data/fbx_mesh_data.cpp b/modules/fbx/data/fbx_mesh_data.cpp deleted file mode 100644 index e1eacc68b3..0000000000 --- a/modules/fbx/data/fbx_mesh_data.cpp +++ /dev/null @@ -1,1435 +0,0 @@ -/*************************************************************************/ -/* fbx_mesh_data.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "fbx_mesh_data.h" - -#include "core/templates/local_vector.h" -#include "scene/resources/importer_mesh.h" -#include "scene/resources/mesh.h" -#include "scene/resources/surface_tool.h" - -#include "thirdparty/misc/polypartition.h" - -template <class T> -T collect_first(const Vector<VertexData<T>> *p_data, T p_fall_back) { - if (p_data->is_empty()) { - return p_fall_back; - } - - return (*p_data)[0].data; -} - -template <class T> -HashMap<int, T> collect_all(const Vector<VertexData<T>> *p_data, HashMap<int, T> p_fall_back) { - if (p_data->is_empty()) { - return p_fall_back; - } - - HashMap<int, T> collection; - for (int i = 0; i < p_data->size(); i += 1) { - const VertexData<T> &vd = (*p_data)[i]; - collection[vd.polygon_index] = vd.data; - } - return collection; -} - -template <class T> -T collect_average(const Vector<VertexData<T>> *p_data, T p_fall_back) { - if (p_data->is_empty()) { - return p_fall_back; - } - - T combined = (*p_data)[0].data; // Make sure the data is always correctly initialized. - print_verbose("size of data: " + itos(p_data->size())); - for (int i = 1; i < p_data->size(); i += 1) { - combined += (*p_data)[i].data; - } - combined = combined / real_t(p_data->size()); - - return combined.normalized(); -} - -HashMap<int, Vector3> collect_normal(const Vector<VertexData<Vector3>> *p_data, HashMap<int, Vector3> p_fall_back) { - if (p_data->is_empty()) { - return p_fall_back; - } - - HashMap<int, Vector3> collection; - for (int i = 0; i < p_data->size(); i += 1) { - const VertexData<Vector3> &vd = (*p_data)[i]; - collection[vd.polygon_index] = vd.data; - } - return collection; -} - -HashMap<int, Vector2> collect_uv(const Vector<VertexData<Vector2>> *p_data, HashMap<int, Vector2> p_fall_back) { - if (p_data->is_empty()) { - return p_fall_back; - } - - HashMap<int, Vector2> collection; - for (int i = 0; i < p_data->size(); i += 1) { - const VertexData<Vector2> &vd = (*p_data)[i]; - collection[vd.polygon_index] = vd.data; - } - return collection; -} - -ImporterMeshInstance3D *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. - const std::vector<const FBXDocParser::Material *> &material_lookup = model->GetMaterials(); - - // TODO: perf hotspot on large files - // this can be a very large copy - std::vector<int> polygon_indices = mesh_geometry->get_polygon_indices(); - std::vector<Vector3> vertices = mesh_geometry->get_vertices(); - - // Phase 1. Parse all FBX data. - HashMap<int, Vector3> normals; - HashMap<int, HashMap<int, Vector3>> normals_raw = extract_per_vertex_data( - vertices.size(), - mesh_geometry->get_edge_map(), - polygon_indices, - mesh_geometry->get_normals(), - &collect_all, - HashMap<int, Vector3>()); - - HashMap<int, Vector2> uvs_0; - HashMap<int, HashMap<int, Vector2>> uvs_0_raw = extract_per_vertex_data( - vertices.size(), - mesh_geometry->get_edge_map(), - polygon_indices, - mesh_geometry->get_uv_0(), - &collect_all, - HashMap<int, Vector2>()); - - HashMap<int, Vector2> uvs_1; - HashMap<int, HashMap<int, Vector2>> uvs_1_raw = extract_per_vertex_data( - vertices.size(), - mesh_geometry->get_edge_map(), - polygon_indices, - mesh_geometry->get_uv_1(), - &collect_all, - HashMap<int, Vector2>()); - - HashMap<int, Color> colors; - HashMap<int, HashMap<int, Color>> colors_raw = extract_per_vertex_data( - vertices.size(), - mesh_geometry->get_edge_map(), - polygon_indices, - mesh_geometry->get_colors(), - &collect_all, - HashMap<int, Color>()); - - // TODO what about tangents? - // TODO what about bi-nomials? - // TODO there is other? - - HashMap<int, SurfaceId> polygon_surfaces = extract_per_polygon( - vertices.size(), - polygon_indices, - mesh_geometry->get_material_allocation_id(), - -1); - - HashMap<String, MorphVertexData> morphs; - extract_morphs(mesh_geometry, morphs); - - // TODO please add skinning. - //mesh_id = mesh_geometry->ID(); - - sanitize_vertex_weights(state); - - // Re organize polygon vertices to to correctly take into account strange - // UVs. - reorganize_vertices( - polygon_indices, - vertices, - normals, - uvs_0, - uvs_1, - colors, - morphs, - normals_raw, - colors_raw, - uvs_0_raw, - uvs_1_raw); - - const int color_count = colors.size(); - print_verbose("Vertex color count: " + itos(color_count)); - - // Make sure that from this moment on the mesh_geometry is no used anymore. - // This is a safety step, because the mesh_geometry data are no more valid - // at this point. - - const int vertex_count = vertices.size(); - - print_verbose("Vertex count: " + itos(vertex_count)); - - // The map key is the material allocator id that is also used as surface id. - HashMap<SurfaceId, SurfaceData> surfaces; - - // Phase 2. For each material create a surface tool (So a different mesh). - { - if (polygon_surfaces.is_empty()) { - // No material, just use the default one with index -1. - // Set -1 to all polygons. - const int polygon_count = count_polygons(polygon_indices); - for (int p = 0; p < polygon_count; p += 1) { - polygon_surfaces[p] = -1; - } - } - - // Create the surface now. - for (const int *polygon_id = polygon_surfaces.next(nullptr); polygon_id != nullptr; polygon_id = polygon_surfaces.next(polygon_id)) { - const int surface_id = polygon_surfaces[*polygon_id]; - if (surfaces.has(surface_id) == false) { - SurfaceData sd; - sd.surface_tool.instantiate(); - sd.surface_tool->begin(Mesh::PRIMITIVE_TRIANGLES); - - if (surface_id < 0) { - // nothing to do - } else if (surface_id < (int)material_lookup.size()) { - const FBXDocParser::Material *mat_mapping = material_lookup.at(surface_id); - const uint64_t mapping_id = mat_mapping->ID(); - if (state.cached_materials.has(mapping_id)) { - sd.material = state.cached_materials[mapping_id]; - } - } else { - WARN_PRINT("out of bounds surface detected, FBX file has corrupt material data"); - } - - surfaces.set(surface_id, sd); - } - } - } - - // Phase 3. Map the vertices relative to each surface, in this way we can - // just insert the vertices that we need per each surface. - { - PolygonId polygon_index = -1; - SurfaceId surface_id = -1; - SurfaceData *surface_data = nullptr; - - for (size_t polygon_vertex = 0; polygon_vertex < polygon_indices.size(); polygon_vertex += 1) { - if (is_start_of_polygon(polygon_indices, polygon_vertex)) { - polygon_index += 1; - ERR_FAIL_COND_V_MSG(polygon_surfaces.has(polygon_index) == false, nullptr, "The FBX file is corrupted, This surface_index is not expected."); - surface_id = polygon_surfaces[polygon_index]; - surface_data = surfaces.getptr(surface_id); - CRASH_COND(surface_data == nullptr); // Can't be null. - } - - const int vertex = get_vertex_from_polygon_vertex(polygon_indices, polygon_vertex); - - // The vertex position in the surface - // Uses a lookup table for speed with large scenes - bool has_polygon_vertex_index = surface_data->lookup_table.has(vertex); - int surface_polygon_vertex_index = -1; - - if (has_polygon_vertex_index) { - surface_polygon_vertex_index = surface_data->lookup_table[vertex]; - } else { - surface_polygon_vertex_index = surface_data->vertices_map.size(); - surface_data->lookup_table[vertex] = surface_polygon_vertex_index; - surface_data->vertices_map.push_back(vertex); - } - - surface_data->surface_polygon_vertex[polygon_index].push_back(surface_polygon_vertex_index); - } - } - - //print_verbose("[debug UV 1] UV1: " + itos(uvs_0.size())); - //print_verbose("[debug UV 2] UV2: " + itos(uvs_1.size())); - - // Phase 4. Per each surface just insert the vertices and add the indices. - for (const SurfaceId *surface_id = surfaces.next(nullptr); surface_id != nullptr; surface_id = surfaces.next(surface_id)) { - SurfaceData *surface = surfaces.getptr(*surface_id); - - // Just add the vertices data. - for (unsigned int i = 0; i < surface->vertices_map.size(); i += 1) { - const Vertex vertex = surface->vertices_map[i]; - - // This must be done before add_vertex because the surface tool is - // expecting this before the st->add_vertex() call - add_vertex(state, - surface->surface_tool, - state.scale, - vertex, - vertices, - normals, - uvs_0, - uvs_1, - colors); - } - - // 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, - *indices, - vertices); - } - } - - // Phase 5. Compose the morphs if any. - for (const SurfaceId *surface_id = surfaces.next(nullptr); surface_id != nullptr; surface_id = surfaces.next(surface_id)) { - SurfaceData *surface = surfaces.getptr(*surface_id); - - for (const String *morph_name = morphs.next(nullptr); morph_name != nullptr; morph_name = morphs.next(morph_name)) { - MorphVertexData *morph_data = morphs.getptr(*morph_name); - - // As said by the docs, this is not supposed to be different than - // vertex_count. - CRASH_COND(morph_data->vertices.size() != vertex_count); - CRASH_COND(morph_data->normals.size() != vertex_count); - - Vector3 *vertices_ptr = morph_data->vertices.ptrw(); - Vector3 *normals_ptr = morph_data->normals.ptrw(); - - Ref<SurfaceTool> morph_st; - morph_st.instantiate(); - 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]; - add_vertex( - state, - morph_st, - state.scale, - vertex, - vertices, - normals, - uvs_0, - uvs_1, - colors, - vertices_ptr[vertex], - normals_ptr[vertex]); - } - - if (state.is_blender_fbx) { - morph_st->generate_normals(); - } - morph_st->generate_tangents(); - surface->morphs.push_back(morph_st->commit_to_arrays()); - } - } - - // Phase 6. Compose the mesh and return it. - Ref<ImporterMesh> mesh; - mesh.instantiate(); - - // Add blend shape info. - for (const String *morph_name = morphs.next(nullptr); morph_name != nullptr; morph_name = morphs.next(morph_name)) { - mesh->add_blend_shape(*morph_name); - } - - // TODO always normalized, Why? - mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_NORMALIZED); - - // Add surfaces. - for (const SurfaceId *surface_id = surfaces.next(nullptr); surface_id != nullptr; surface_id = surfaces.next(surface_id)) { - SurfaceData *surface = surfaces.getptr(*surface_id); - - if (state.is_blender_fbx) { - surface->surface_tool->generate_normals(); - } - // you can't generate them without a valid uv map. - if (uvs_0_raw.size() > 0) { - surface->surface_tool->generate_tangents(); - } - - Array mesh_array = surface->surface_tool->commit_to_arrays(); - Array blend_shapes = surface->morphs; - - if (surface->material.is_valid()) { - mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES, mesh_array, blend_shapes, Dictionary(), surface->material, surface->material->get_name()); - } else { - mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES, mesh_array, blend_shapes); - } - } - - ImporterMeshInstance3D *godot_mesh = memnew(ImporterMeshInstance3D); - 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; -} - -void FBXMeshData::sanitize_vertex_weights(const ImportState &state) { - const int max_vertex_influence_count = RS::ARRAY_WEIGHTS_SIZE; - Map<int, int> skeleton_to_skin_bind_id; - // TODO: error's need added - const FBXDocParser::Skin *fbx_skin = mesh_geometry->DeformerSkin(); - - if (fbx_skin == nullptr || fbx_skin->Clusters().size() == 0) { - return; // do nothing - } - - // - // Precalculate the skin cluster mapping - // - - int bind_id = 0; - for (const FBXDocParser::Cluster *cluster : fbx_skin->Clusters()) { - ERR_CONTINUE_MSG(!state.fbx_bone_map.has(cluster->TargetNode()->ID()), "Missing bone map for cluster target node with id " + uitos(cluster->TargetNode()->ID()) + "."); - Ref<FBXBone> bone = state.fbx_bone_map[cluster->TargetNode()->ID()]; - skeleton_to_skin_bind_id.insert(bone->godot_bone_id, bind_id); - bind_id++; - } - - for (const Vertex *v = vertex_weights.next(nullptr); v != nullptr; v = vertex_weights.next(v)) { - VertexWeightMapping *vm = vertex_weights.getptr(*v); - ERR_CONTINUE(vm->bones.size() != vm->weights.size()); // No message, already checked. - ERR_CONTINUE(vm->bones_ref.size() != vm->weights.size()); // No message, already checked. - - const int initial_size = vm->weights.size(); - { - // Init bone id - int *bones_ptr = vm->bones.ptrw(); - Ref<FBXBone> *bones_ref_ptr = vm->bones_ref.ptrw(); - - for (int i = 0; i < vm->weights.size(); i += 1) { - // At this point this is not possible because the skeleton is already initialized. - CRASH_COND(bones_ref_ptr[i]->godot_bone_id == -2); - bones_ptr[i] = skeleton_to_skin_bind_id[bones_ref_ptr[i]->godot_bone_id]; - } - - // From this point on the data is no more valid. - vm->bones_ref.clear(); - } - - { - // Sort - float *weights_ptr = vm->weights.ptrw(); - int *bones_ptr = vm->bones.ptrw(); - for (int i = 0; i < vm->weights.size(); i += 1) { - for (int x = i + 1; x < vm->weights.size(); x += 1) { - if (weights_ptr[i] < weights_ptr[x]) { - SWAP(weights_ptr[i], weights_ptr[x]); - SWAP(bones_ptr[i], bones_ptr[x]); - } - } - } - } - - { - // Resize - vm->weights.resize(max_vertex_influence_count); - vm->bones.resize(max_vertex_influence_count); - float *weights_ptr = vm->weights.ptrw(); - int *bones_ptr = vm->bones.ptrw(); - for (int i = initial_size; i < max_vertex_influence_count; i += 1) { - weights_ptr[i] = 0.0; - bones_ptr[i] = 0; - } - - // Normalize - real_t sum = 0.0; - for (int i = 0; i < max_vertex_influence_count; i += 1) { - sum += weights_ptr[i]; - } - if (sum > 0.0) { - for (int i = 0; i < vm->weights.size(); i += 1) { - weights_ptr[i] = weights_ptr[i] / sum; - } - } - } - } -} - -void FBXMeshData::reorganize_vertices( - // TODO: perf hotspot on insane files - std::vector<int> &r_polygon_indices, - std::vector<Vector3> &r_vertices, - HashMap<int, Vector3> &r_normals, - HashMap<int, Vector2> &r_uv_1, - HashMap<int, Vector2> &r_uv_2, - HashMap<int, Color> &r_color, - HashMap<String, MorphVertexData> &r_morphs, - HashMap<int, HashMap<int, Vector3>> &r_normals_raw, - HashMap<int, HashMap<int, Color>> &r_colors_raw, - HashMap<int, HashMap<int, Vector2>> &r_uv_1_raw, - HashMap<int, HashMap<int, Vector2>> &r_uv_2_raw) { - // Key: OldVertex; Value: [New vertices]; - HashMap<int, Vector<int>> duplicated_vertices; - - PolygonId polygon_index = -1; - for (int pv = 0; pv < (int)r_polygon_indices.size(); pv += 1) { - if (is_start_of_polygon(r_polygon_indices, pv)) { - polygon_index += 1; - } - const Vertex index = get_vertex_from_polygon_vertex(r_polygon_indices, pv); - - bool need_duplication = false; - Vector2 this_vert_poly_uv1 = Vector2(); - Vector2 this_vert_poly_uv2 = Vector2(); - Vector3 this_vert_poly_normal = Vector3(); - Color this_vert_poly_color = Color(); - - // Take the normal and see if we need to duplicate this polygon. - if (r_normals_raw.has(index)) { - const HashMap<PolygonId, Vector3> *nrml_arr = r_normals_raw.getptr(index); - - if (nrml_arr->has(polygon_index)) { - this_vert_poly_normal = nrml_arr->get(polygon_index); - } else if (nrml_arr->has(-1)) { - this_vert_poly_normal = nrml_arr->get(-1); - } else { - print_error("invalid normal detected: " + itos(index) + " polygon index: " + itos(polygon_index)); - for (const PolygonId *pid = nrml_arr->next(nullptr); pid != nullptr; pid = nrml_arr->next(pid)) { - print_verbose("debug contents key: " + itos(*pid)); - - if (nrml_arr->has(*pid)) { - print_verbose("contents valid: " + nrml_arr->get(*pid)); - } - } - } - - // Now, check if we need to duplicate it. - for (const PolygonId *pid = nrml_arr->next(nullptr); pid != nullptr; pid = nrml_arr->next(pid)) { - if (*pid == polygon_index) { - continue; - } - - const Vector3 vert_poly_normal = *nrml_arr->getptr(*pid); - if (!vert_poly_normal.is_equal_approx(this_vert_poly_normal)) { - // Yes this polygon need duplication. - need_duplication = true; - break; - } - } - } - - // TODO: make me vertex color - // Take the normal and see if we need to duplicate this polygon. - if (r_colors_raw.has(index)) { - const HashMap<PolygonId, Color> *color_arr = r_colors_raw.getptr(index); - - if (color_arr->has(polygon_index)) { - this_vert_poly_color = color_arr->get(polygon_index); - } else if (color_arr->has(-1)) { - this_vert_poly_color = color_arr->get(-1); - } else { - print_error("invalid color detected: " + itos(index) + " polygon index: " + itos(polygon_index)); - for (const PolygonId *pid = color_arr->next(nullptr); pid != nullptr; pid = color_arr->next(pid)) { - print_verbose("debug contents key: " + itos(*pid)); - - if (color_arr->has(*pid)) { - print_verbose("contents valid: " + color_arr->get(*pid)); - } - } - } - - // Now, check if we need to duplicate it. - for (const PolygonId *pid = color_arr->next(nullptr); pid != nullptr; pid = color_arr->next(pid)) { - if (*pid == polygon_index) { - continue; - } - - const Color vert_poly_color = *color_arr->getptr(*pid); - if (!this_vert_poly_color.is_equal_approx(vert_poly_color)) { - // Yes this polygon need duplication. - need_duplication = true; - break; - } - } - } - - // Take the UV1 and UV2 and see if we need to duplicate this polygon. - { - HashMap<int, HashMap<int, Vector2>> *uv_raw = &r_uv_1_raw; - Vector2 *this_vert_poly_uv = &this_vert_poly_uv1; - for (int kk = 0; kk < 2; kk++) { - if (uv_raw->has(index)) { - const HashMap<PolygonId, Vector2> *uvs = uv_raw->getptr(index); - - if (uvs->has(polygon_index)) { - // This Polygon has its own uv. - (*this_vert_poly_uv) = *uvs->getptr(polygon_index); - - // Check if we need to duplicate it. - for (const PolygonId *pid = uvs->next(nullptr); pid != nullptr; pid = uvs->next(pid)) { - if (*pid == polygon_index) { - continue; - } - const Vector2 vert_poly_uv = *uvs->getptr(*pid); - if (!vert_poly_uv.is_equal_approx(*this_vert_poly_uv)) { - // Yes this polygon need duplication. - need_duplication = true; - break; - } - } - } else if (uvs->has(-1)) { - // It has the default UV. - (*this_vert_poly_uv) = *uvs->getptr(-1); - } else if (uvs->size() > 0) { - // No uv, this is strange, just take the first and duplicate. - (*this_vert_poly_uv) = *uvs->getptr(*uvs->next(nullptr)); - WARN_PRINT("No UVs for this polygon, while there is no default and some other polygons have it. This FBX file may be corrupted."); - } - } - uv_raw = &r_uv_2_raw; - this_vert_poly_uv = &this_vert_poly_uv2; - } - } - - // If we want to duplicate it, Let's see if we already duplicated this - // vertex. - if (need_duplication) { - if (duplicated_vertices.has(index)) { - Vertex similar_vertex = -1; - // Let's see if one of the new vertices has the same data of this. - const Vector<int> *new_vertices = duplicated_vertices.getptr(index); - for (int j = 0; j < new_vertices->size(); j += 1) { - const Vertex new_vertex = (*new_vertices)[j]; - bool same_uv1 = false; - bool same_uv2 = false; - bool same_normal = false; - bool same_color = false; - - if (r_uv_1.has(new_vertex)) { - if ((this_vert_poly_uv1 - (*r_uv_1.getptr(new_vertex))).length_squared() <= CMP_EPSILON) { - same_uv1 = true; - } - } - - if (r_uv_2.has(new_vertex)) { - if ((this_vert_poly_uv2 - (*r_uv_2.getptr(new_vertex))).length_squared() <= CMP_EPSILON) { - same_uv2 = true; - } - } - - if (r_color.has(new_vertex)) { - if (this_vert_poly_color.is_equal_approx((*r_color.getptr(new_vertex)))) { - same_color = true; - } - } - - if (r_normals.has(new_vertex)) { - if ((this_vert_poly_normal - (*r_normals.getptr(new_vertex))).length_squared() <= CMP_EPSILON) { - same_uv2 = true; - } - } - - if (same_uv1 && same_uv2 && same_normal && same_color) { - similar_vertex = new_vertex; - break; - } - } - - if (similar_vertex != -1) { - // Update polygon. - if (is_end_of_polygon(r_polygon_indices, pv)) { - r_polygon_indices[pv] = ~similar_vertex; - } else { - r_polygon_indices[pv] = similar_vertex; - } - need_duplication = false; - } - } - } - - if (need_duplication) { - const Vertex old_index = index; - const Vertex new_index = r_vertices.size(); - - // Polygon index. - if (is_end_of_polygon(r_polygon_indices, pv)) { - r_polygon_indices[pv] = ~new_index; - } else { - r_polygon_indices[pv] = new_index; - } - - // Vertex position. - r_vertices.push_back(r_vertices[old_index]); - - // Normals - if (r_normals_raw.has(old_index)) { - r_normals.set(new_index, this_vert_poly_normal); - r_normals_raw.getptr(old_index)->erase(polygon_index); - r_normals_raw[new_index][polygon_index] = this_vert_poly_normal; - } - - // Vertex Color - if (r_colors_raw.has(old_index)) { - r_color.set(new_index, this_vert_poly_color); - r_colors_raw.getptr(old_index)->erase(polygon_index); - r_colors_raw[new_index][polygon_index] = this_vert_poly_color; - } - - // UV 0 - if (r_uv_1_raw.has(old_index)) { - r_uv_1.set(new_index, this_vert_poly_uv1); - r_uv_1_raw.getptr(old_index)->erase(polygon_index); - r_uv_1_raw[new_index][polygon_index] = this_vert_poly_uv1; - } - - // UV 1 - if (r_uv_2_raw.has(old_index)) { - r_uv_2.set(new_index, this_vert_poly_uv2); - r_uv_2_raw.getptr(old_index)->erase(polygon_index); - r_uv_2_raw[new_index][polygon_index] = this_vert_poly_uv2; - } - - // Morphs - for (const String *mname = r_morphs.next(nullptr); mname != nullptr; mname = r_morphs.next(mname)) { - MorphVertexData *d = r_morphs.getptr(*mname); - // This can't never happen. - CRASH_COND(d == nullptr); - if (d->vertices.size() > old_index) { - d->vertices.push_back(d->vertices[old_index]); - } - if (d->normals.size() > old_index) { - d->normals.push_back(d->normals[old_index]); - } - } - - if (vertex_weights.has(old_index)) { - vertex_weights.set(new_index, vertex_weights[old_index]); - } - - duplicated_vertices[old_index].push_back(new_index); - } else { - if (r_normals_raw.has(index) && - r_normals.has(index) == false) { - r_normals.set(index, this_vert_poly_normal); - } - - if (r_colors_raw.has(index) && r_color.has(index) == false) { - r_color.set(index, this_vert_poly_color); - } - - if (r_uv_1_raw.has(index) && - r_uv_1.has(index) == false) { - r_uv_1.set(index, this_vert_poly_uv1); - } - - if (r_uv_2_raw.has(index) && - r_uv_2.has(index) == false) { - r_uv_2.set(index, this_vert_poly_uv2); - } - } - } -} - -void FBXMeshData::add_vertex( - const ImportState &state, - Ref<SurfaceTool> p_surface_tool, - real_t p_scale, - Vertex p_vertex, - const std::vector<Vector3> &p_vertices_position, - const HashMap<int, Vector3> &p_normals, - const HashMap<int, Vector2> &p_uvs_0, - const HashMap<int, Vector2> &p_uvs_1, - const HashMap<int, Color> &p_colors, - const Vector3 &p_morph_value, - const Vector3 &p_morph_normal) { - ERR_FAIL_INDEX_MSG(p_vertex, (Vertex)p_vertices_position.size(), "FBX file is corrupted, the position of the vertex can't be retrieved."); - - if (p_normals.has(p_vertex) && !state.is_blender_fbx) { - p_surface_tool->set_normal(p_normals[p_vertex] + p_morph_normal); - } - - if (p_uvs_0.has(p_vertex)) { - //print_verbose("uv1: [" + itos(p_vertex) + "] " + p_uvs_0[p_vertex]); - // Inverts Y UV. - p_surface_tool->set_uv(Vector2(p_uvs_0[p_vertex].x, 1 - p_uvs_0[p_vertex].y)); - } - - if (p_uvs_1.has(p_vertex)) { - //print_verbose("uv2: [" + itos(p_vertex) + "] " + p_uvs_1[p_vertex]); - // Inverts Y UV. - p_surface_tool->set_uv2(Vector2(p_uvs_1[p_vertex].x, 1 - p_uvs_1[p_vertex].y)); - } - - if (p_colors.has(p_vertex)) { - p_surface_tool->set_color(p_colors[p_vertex]); - } - - // TODO what about binormals? - // TODO there is other? - - if (vertex_weights.has(p_vertex)) { - // Let's extract the weight info. - const VertexWeightMapping *vm = vertex_weights.getptr(p_vertex); - const Vector<int> &bones = vm->bones; - - // the bug is that the bone idx is wrong because it is not ref'ing the skin. - - if (bones.size() > RS::ARRAY_WEIGHTS_SIZE) { - print_error("[weight overflow detected]"); - } - - p_surface_tool->set_weights(vm->weights); - // 0 1 2 3 4 5 6 7 < local skeleton / skin for mesh - // 0 1 2 3 4 5 6 7 8 9 10 < actual skeleton with all joints - p_surface_tool->set_bones(bones); - } - - // The surface tool want the vertex position as last thing. - p_surface_tool->add_vertex((p_vertices_position[p_vertex] + p_morph_value) * p_scale); -} - -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]); - st->add_index(p_polygon_vertex[0]); - st->add_index(p_polygon_vertex[0]); - return; - } else if (polygon_vertex_count == 2) { - // line to triangle - st->add_index(p_polygon_vertex[1]); - st->add_index(p_polygon_vertex[1]); - st->add_index(p_polygon_vertex[0]); - return; - } else if (polygon_vertex_count == 3) { - // triangle to triangle - st->add_index(p_polygon_vertex[0]); - st->add_index(p_polygon_vertex[2]); - st->add_index(p_polygon_vertex[1]); - return; - } else if (polygon_vertex_count == 4) { - // quad to triangle - this code is awesome for import times - // it prevents triangles being generated slowly - st->add_index(p_polygon_vertex[0]); - st->add_index(p_polygon_vertex[2]); - st->add_index(p_polygon_vertex[1]); - st->add_index(p_polygon_vertex[2]); - st->add_index(p_polygon_vertex[0]); - st->add_index(p_polygon_vertex[3]); - return; - } else { - // non triangulated - we must run the triangulation algorithm - bool is_simple_convex = false; - // this code is 'slow' but required it triangulates all the unsupported geometry. - // Doesn't allow for bigger polygons because those are unlikely be convex - if (polygon_vertex_count <= 6) { - // Start from true, check if it's false. - is_simple_convex = true; - Vector3 first_vec; - for (int i = 0; i < polygon_vertex_count; i += 1) { - 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; - - const Vector3 res = edge1.normalized().cross(edge2.normalized()).normalized(); - if (i == 0) { - first_vec = res; - } else { - if (first_vec.dot(res) < 0.0) { - // Ok we found an angle that is not the same dir of the - // others. - is_simple_convex = false; - break; - } - } - } - } - - if (is_simple_convex) { - // This is a convex polygon, so just triangulate it. - for (int i = 0; i < (polygon_vertex_count - 2); i += 1) { - st->add_index(p_polygon_vertex[2 + i]); - st->add_index(p_polygon_vertex[1 + i]); - st->add_index(p_polygon_vertex[0]); - } - return; - } - } - - { - // This is a concave polygon. - - std::vector<Vector3> poly_vertices(polygon_vertex_count); - for (int i = 0; i < polygon_vertex_count; i += 1) { - poly_vertices[i] = p_vertices[surface->vertices_map[p_polygon_vertex[i]]]; - } - - const Vector3 poly_norm = get_poly_normal(poly_vertices); - if (poly_norm.length_squared() <= CMP_EPSILON) { - ERR_FAIL_COND_MSG(poly_norm.length_squared() <= CMP_EPSILON, "The normal of this poly was not computed. Is this FBX file corrupted."); - } - - // Select the plan coordinate. - int axis_1_coord = 0; - int axis_2_coord = 1; - { - real_t inv = poly_norm.z; - - const real_t axis_x = ABS(poly_norm.x); - const real_t axis_y = ABS(poly_norm.y); - const real_t axis_z = ABS(poly_norm.z); - - if (axis_x > axis_y) { - if (axis_x > axis_z) { - // For the most part the normal point toward X. - axis_1_coord = 1; - axis_2_coord = 2; - inv = poly_norm.x; - } - } else if (axis_y > axis_z) { - // For the most part the normal point toward Y. - axis_1_coord = 2; - axis_2_coord = 0; - inv = poly_norm.y; - } - - // Swap projection axes to take the negated projection vector into account - if (inv < 0.0f) { - SWAP(axis_1_coord, axis_2_coord); - } - } - - TPPLPoly tppl_poly; - tppl_poly.Init(polygon_vertex_count); - std::vector<Vector2> projected_vertices(polygon_vertex_count); - for (int i = 0; i < polygon_vertex_count; i += 1) { - const Vector2 pv(poly_vertices[i][axis_1_coord], poly_vertices[i][axis_2_coord]); - projected_vertices[i] = pv; - tppl_poly.GetPoint(i) = pv; - } - tppl_poly.SetOrientation(TPPL_ORIENTATION_CCW); - - List<TPPLPoly> out_poly; - - TPPLPartition tppl_partition; - if (tppl_partition.Triangulate_OPT(&tppl_poly, &out_poly) == 0) { // Good result. - if (tppl_partition.Triangulate_EC(&tppl_poly, &out_poly) == 0) { // Medium result. - if (tppl_partition.Triangulate_MONO(&tppl_poly, &out_poly) == 0) { // Really poor result. - ERR_FAIL_MSG("The triangulation of this polygon failed, please try to triangulate your mesh or check if it has broken polygons."); - } - } - } - - std::vector<Vector2> tris(out_poly.size()); - for (List<TPPLPoly>::Element *I = out_poly.front(); I; I = I->next()) { - TPPLPoly &tp = I->get(); - - ERR_FAIL_COND_MSG(tp.GetNumPoints() != 3, "The triangulator returned more points, how this is possible?"); - // Find Index - for (int i = 2; i >= 0; i -= 1) { - const Vector2 vertex = tp.GetPoint(i); - bool done = false; - // Find Index - for (int y = 0; y < polygon_vertex_count; y += 1) { - if ((projected_vertices[y] - vertex).length_squared() <= CMP_EPSILON) { - // This seems the right vertex - st->add_index(p_polygon_vertex[y]); - done = true; - break; - } - } - ERR_FAIL_COND(done == false); - } - } - } -} - -void FBXMeshData::gen_weight_info(Ref<SurfaceTool> st, Vertex vertex_id) const { - if (vertex_weights.is_empty()) { - return; - } - - if (vertex_weights.has(vertex_id)) { - // Let's extract the weight info. - const VertexWeightMapping *vm = vertex_weights.getptr(vertex_id); - st->set_weights(vm->weights); - st->set_bones(vm->bones); - } -} - -int FBXMeshData::get_vertex_from_polygon_vertex(const std::vector<int> &p_polygon_indices, int p_index) const { - if (p_index < 0 || p_index >= (int)p_polygon_indices.size()) { - return -1; - } - - const int vertex = p_polygon_indices[p_index]; - if (vertex >= 0) { - return vertex; - } else { - // Negative numbers are the end of the face, reversing the bits is - // possible to obtain the positive correct vertex number. - return ~vertex; - } -} - -bool FBXMeshData::is_end_of_polygon(const std::vector<int> &p_polygon_indices, int p_index) const { - if (p_index < 0 || p_index >= (int)p_polygon_indices.size()) { - return false; - } - - const int vertex = p_polygon_indices[p_index]; - - // If the index is negative this is the end of the Polygon. - return vertex < 0; -} - -bool FBXMeshData::is_start_of_polygon(const std::vector<int> &p_polygon_indices, int p_index) const { - if (p_index < 0 || p_index >= (int)p_polygon_indices.size()) { - return false; - } - - if (p_index == 0) { - return true; - } - - // If the previous indices is negative this is the begin of a new Polygon. - return p_polygon_indices[p_index - 1] < 0; -} - -int FBXMeshData::count_polygons(const std::vector<int> &p_polygon_indices) const { - // The negative numbers define the end of the polygon. Counting the amount of - // negatives the numbers of polygons are obtained. - int count = 0; - for (size_t i = 0; i < p_polygon_indices.size(); i += 1) { - if (p_polygon_indices[i] < 0) { - count += 1; - } - } - return count; -} - -template <class R, class T> -HashMap<int, R> FBXMeshData::extract_per_vertex_data( - int p_vertex_count, - const std::vector<FBXDocParser::MeshGeometry::Edge> &p_edge_map, - const std::vector<int> &p_mesh_indices, - const FBXDocParser::MeshGeometry::MappingData<T> &p_mapping_data, - R (*collector_function)(const Vector<VertexData<T>> *p_vertex_data, R p_fall_back), - R p_fall_back) const { - /* When index_to_direct is set - * index size is 184 ( contains index for the data array [values 0, 96] ) - * data size is 96 (contains uv coordinates) - * this means index is simple data reduction basically - */ - //// - if (p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index_to_direct && p_mapping_data.index.size() == 0) { - print_verbose("debug count: index size: " + itos(p_mapping_data.index.size()) + ", data size: " + itos(p_mapping_data.data.size())); - print_verbose("vertex indices count: " + itos(p_mesh_indices.size())); - print_verbose("Edge map size: " + itos(p_edge_map.size())); - } - - ERR_FAIL_COND_V_MSG(p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index_to_direct && p_mapping_data.index.size() == 0, (HashMap<int, R>()), "FBX importer needs to map correctly to this field, please specify the override index name to fix this problem!"); - ERR_FAIL_COND_V_MSG(p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index && p_mapping_data.index.size() == 0, (HashMap<int, R>()), "The FBX seems corrupted"); - - // Aggregate vertex data. - HashMap<Vertex, Vector<VertexData<T>>> aggregate_vertex_data; - - switch (p_mapping_data.map_type) { - case FBXDocParser::MeshGeometry::MapType::none: { - // No data nothing to do. - return (HashMap<int, R>()); - } - case FBXDocParser::MeshGeometry::MapType::vertex: { - ERR_FAIL_COND_V_MSG(p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index_to_direct, (HashMap<int, R>()), "We will support in future"); - - if (p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::direct) { - // The data is mapped per vertex directly. - ERR_FAIL_COND_V_MSG((int)p_mapping_data.data.size() != p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR01"); - for (size_t vertex_index = 0; vertex_index < p_mapping_data.data.size(); vertex_index += 1) { - aggregate_vertex_data[vertex_index].push_back({ -1, p_mapping_data.data[vertex_index] }); - } - } else { - // The data is mapped per vertex using a reference. - // The indices array, contains a *reference_id for each vertex. - // * Note that the reference_id is the id of data into the data array. - // - // https://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_layer_element_html - ERR_FAIL_COND_V_MSG((int)p_mapping_data.index.size() != p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR02"); - for (size_t vertex_index = 0; vertex_index < p_mapping_data.index.size(); vertex_index += 1) { - ERR_FAIL_INDEX_V_MSG(p_mapping_data.index[vertex_index], (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR03."); - aggregate_vertex_data[vertex_index].push_back({ -1, p_mapping_data.data[p_mapping_data.index[vertex_index]] }); - } - } - } break; - case FBXDocParser::MeshGeometry::MapType::polygon_vertex: { - if (p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index_to_direct) { - // The data is mapped using each index from the indexes array then direct to the data (data reduction algorithm) - ERR_FAIL_COND_V_MSG((int)p_mesh_indices.size() != (int)p_mapping_data.index.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR04"); - int polygon_id = -1; - for (size_t polygon_vertex_index = 0; polygon_vertex_index < p_mapping_data.index.size(); polygon_vertex_index += 1) { - if (is_start_of_polygon(p_mesh_indices, polygon_vertex_index)) { - polygon_id += 1; - } - const int vertex_index = get_vertex_from_polygon_vertex(p_mesh_indices, polygon_vertex_index); - ERR_FAIL_COND_V_MSG(vertex_index < 0, (HashMap<int, R>()), "FBX file corrupted: #ERR05"); - ERR_FAIL_COND_V_MSG(vertex_index >= p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR06"); - const int index_to_direct = p_mapping_data.index[polygon_vertex_index]; - T value = p_mapping_data.data[index_to_direct]; - aggregate_vertex_data[vertex_index].push_back({ polygon_id, value }); - } - } else if (p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::direct) { - // The data are mapped per polygon vertex directly. - ERR_FAIL_COND_V_MSG((int)p_mesh_indices.size() != (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR04"); - int polygon_id = -1; - for (size_t polygon_vertex_index = 0; polygon_vertex_index < p_mapping_data.data.size(); polygon_vertex_index += 1) { - if (is_start_of_polygon(p_mesh_indices, polygon_vertex_index)) { - polygon_id += 1; - } - const int vertex_index = get_vertex_from_polygon_vertex(p_mesh_indices, polygon_vertex_index); - ERR_FAIL_COND_V_MSG(vertex_index < 0, (HashMap<int, R>()), "FBX file corrupted: #ERR05"); - ERR_FAIL_COND_V_MSG(vertex_index >= p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR06"); - - aggregate_vertex_data[vertex_index].push_back({ polygon_id, p_mapping_data.data[polygon_vertex_index] }); - } - } else { - // The data is mapped per polygon_vertex using a reference. - // The indices array, contains a *reference_id for each polygon_vertex. - // * Note that the reference_id is the id of data into the data array. - // - // https://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_layer_element_html - ERR_FAIL_COND_V_MSG(p_mesh_indices.size() != p_mapping_data.index.size(), (HashMap<int, R>()), "FBX file corrupted: #ERR7"); - int polygon_id = -1; - for (size_t polygon_vertex_index = 0; polygon_vertex_index < p_mapping_data.index.size(); polygon_vertex_index += 1) { - if (is_start_of_polygon(p_mesh_indices, polygon_vertex_index)) { - polygon_id += 1; - } - const int vertex_index = get_vertex_from_polygon_vertex(p_mesh_indices, polygon_vertex_index); - ERR_FAIL_COND_V_MSG(vertex_index < 0, (HashMap<int, R>()), "FBX file corrupted: #ERR8"); - ERR_FAIL_COND_V_MSG(vertex_index >= p_vertex_count, (HashMap<int, R>()), "FBX file seems corrupted: #ERR9."); - ERR_FAIL_COND_V_MSG(p_mapping_data.index[polygon_vertex_index] < 0, (HashMap<int, R>()), "FBX file seems corrupted: #ERR10."); - ERR_FAIL_COND_V_MSG(p_mapping_data.index[polygon_vertex_index] >= (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR11."); - aggregate_vertex_data[vertex_index].push_back({ polygon_id, p_mapping_data.data[p_mapping_data.index[polygon_vertex_index]] }); - } - } - } break; - case FBXDocParser::MeshGeometry::MapType::polygon: { - if (p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::direct) { - // The data are mapped per polygon directly. - const int polygon_count = count_polygons(p_mesh_indices); - ERR_FAIL_COND_V_MSG(polygon_count != (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR12"); - - // Advance each polygon vertex, each new polygon advance the polygon index. - int polygon_index = -1; - for (size_t polygon_vertex_index = 0; - polygon_vertex_index < p_mesh_indices.size(); - polygon_vertex_index += 1) { - if (is_start_of_polygon(p_mesh_indices, polygon_vertex_index)) { - polygon_index += 1; - ERR_FAIL_INDEX_V_MSG(polygon_index, (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR13"); - } - - const int vertex_index = get_vertex_from_polygon_vertex(p_mesh_indices, polygon_vertex_index); - ERR_FAIL_INDEX_V_MSG(vertex_index, p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR14"); - - aggregate_vertex_data[vertex_index].push_back({ polygon_index, p_mapping_data.data[polygon_index] }); - } - ERR_FAIL_COND_V_MSG((polygon_index + 1) != polygon_count, (HashMap<int, R>()), "FBX file seems corrupted: #ERR16. Not all Polygons are present in the file."); - } else { - // The data is mapped per polygon using a reference. - // The indices array, contains a *reference_id for each polygon. - // * Note that the reference_id is the id of data into the data array. - // - // https://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_layer_element_html - const int polygon_count = count_polygons(p_mesh_indices); - ERR_FAIL_COND_V_MSG(polygon_count != (int)p_mapping_data.index.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR17"); - - // Advance each polygon vertex, each new polygon advance the polygon index. - int polygon_index = -1; - for (size_t polygon_vertex_index = 0; - polygon_vertex_index < p_mesh_indices.size(); - polygon_vertex_index += 1) { - if (is_start_of_polygon(p_mesh_indices, polygon_vertex_index)) { - polygon_index += 1; - ERR_FAIL_INDEX_V_MSG(polygon_index, (int)p_mapping_data.index.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR18"); - ERR_FAIL_INDEX_V_MSG(p_mapping_data.index[polygon_index], (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR19"); - } - - const int vertex_index = get_vertex_from_polygon_vertex(p_mesh_indices, polygon_vertex_index); - ERR_FAIL_INDEX_V_MSG(vertex_index, p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR20"); - - aggregate_vertex_data[vertex_index].push_back({ polygon_index, p_mapping_data.data[p_mapping_data.index[polygon_index]] }); - } - ERR_FAIL_COND_V_MSG((polygon_index + 1) != polygon_count, (HashMap<int, R>()), "FBX file seems corrupted: #ERR22. Not all Polygons are present in the file."); - } - } break; - case FBXDocParser::MeshGeometry::MapType::edge: { - if (p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::direct) { - // The data are mapped per edge directly. - ERR_FAIL_COND_V_MSG(p_edge_map.size() != p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR23"); - for (size_t edge_index = 0; edge_index < p_mapping_data.data.size(); edge_index += 1) { - const FBXDocParser::MeshGeometry::Edge edge = FBXDocParser::MeshGeometry::get_edge(p_edge_map, edge_index); - ERR_FAIL_INDEX_V_MSG(edge.vertex_0, p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR24"); - ERR_FAIL_INDEX_V_MSG(edge.vertex_1, p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR25"); - ERR_FAIL_INDEX_V_MSG(edge.vertex_0, (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file corrupted: #ERR26"); - ERR_FAIL_INDEX_V_MSG(edge.vertex_1, (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file corrupted: #ERR27"); - aggregate_vertex_data[edge.vertex_0].push_back({ -1, p_mapping_data.data[edge_index] }); - aggregate_vertex_data[edge.vertex_1].push_back({ -1, p_mapping_data.data[edge_index] }); - } - } else { - // The data is mapped per edge using a reference. - // The indices array, contains a *reference_id for each polygon. - // * Note that the reference_id is the id of data into the data array. - // - // https://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_layer_element_html - ERR_FAIL_COND_V_MSG(p_edge_map.size() != p_mapping_data.index.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR28"); - for (size_t edge_index = 0; edge_index < p_mapping_data.data.size(); edge_index += 1) { - const FBXDocParser::MeshGeometry::Edge edge = FBXDocParser::MeshGeometry::get_edge(p_edge_map, edge_index); - ERR_FAIL_INDEX_V_MSG(edge.vertex_0, p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR29"); - ERR_FAIL_INDEX_V_MSG(edge.vertex_1, p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR30"); - ERR_FAIL_INDEX_V_MSG(edge.vertex_0, (int)p_mapping_data.index.size(), (HashMap<int, R>()), "FBX file corrupted: #ERR31"); - ERR_FAIL_INDEX_V_MSG(edge.vertex_1, (int)p_mapping_data.index.size(), (HashMap<int, R>()), "FBX file corrupted: #ERR32"); - ERR_FAIL_INDEX_V_MSG(p_mapping_data.index[edge.vertex_0], (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file corrupted: #ERR33"); - ERR_FAIL_INDEX_V_MSG(p_mapping_data.index[edge.vertex_1], (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file corrupted: #ERR34"); - aggregate_vertex_data[edge.vertex_0].push_back({ -1, p_mapping_data.data[p_mapping_data.index[edge_index]] }); - aggregate_vertex_data[edge.vertex_1].push_back({ -1, p_mapping_data.data[p_mapping_data.index[edge_index]] }); - } - } - } break; - case FBXDocParser::MeshGeometry::MapType::all_the_same: { - // No matter the mode, no matter the data size; The first always win - // and is set to all the vertices. - ERR_FAIL_COND_V_MSG(p_mapping_data.data.size() <= 0, (HashMap<int, R>()), "FBX file seems corrupted: #ERR35"); - if (p_mapping_data.data.size() > 0) { - for (int vertex_index = 0; vertex_index < p_vertex_count; vertex_index += 1) { - aggregate_vertex_data[vertex_index].push_back({ -1, p_mapping_data.data[0] }); - } - } - } break; - } - - if (aggregate_vertex_data.size() == 0) { - return (HashMap<int, R>()); - } - - // A map is used because turns out that the some FBX file are not well organized - // with vertices well compacted. Using a map allows avoid those issues. - HashMap<Vertex, R> result; - - // Aggregate the collected data. - for (const Vertex *index = aggregate_vertex_data.next(nullptr); index != nullptr; index = aggregate_vertex_data.next(index)) { - Vector<VertexData<T>> *aggregated_vertex = aggregate_vertex_data.getptr(*index); - // This can't be null because we are just iterating. - CRASH_COND(aggregated_vertex == nullptr); - - ERR_FAIL_INDEX_V_MSG(0, aggregated_vertex->size(), (HashMap<int, R>()), "The FBX file is corrupted, No valid data for this vertex index."); - result[*index] = collector_function(aggregated_vertex, p_fall_back); - } - - // Sanitize the data now, if the file is broken we can try import it anyway. - bool problem_found = false; - for (size_t i = 0; i < p_mesh_indices.size(); i += 1) { - const Vertex vertex = get_vertex_from_polygon_vertex(p_mesh_indices, i); - if (result.has(vertex) == false) { - result[vertex] = p_fall_back; - problem_found = true; - } - } - if (problem_found) { - WARN_PRINT("Some data is missing, this FBX file may be corrupted: #WARN0."); - } - - return result; -} - -template <class T> -HashMap<int, T> FBXMeshData::extract_per_polygon( - int p_vertex_count, - const std::vector<int> &p_polygon_indices, - const FBXDocParser::MeshGeometry::MappingData<T> &p_fbx_data, - T p_fallback_value) const { - ERR_FAIL_COND_V_MSG(p_fbx_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index_to_direct && p_fbx_data.data.size() == 0, (HashMap<int, T>()), "invalid index to direct array"); - ERR_FAIL_COND_V_MSG(p_fbx_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index && p_fbx_data.index.size() == 0, (HashMap<int, T>()), "The FBX seems corrupted"); - - const int polygon_count = count_polygons(p_polygon_indices); - - // Aggregate vertex data. - HashMap<int, Vector<T>> aggregate_polygon_data; - - switch (p_fbx_data.map_type) { - case FBXDocParser::MeshGeometry::MapType::none: { - // No data nothing to do. - return (HashMap<int, T>()); - } - case FBXDocParser::MeshGeometry::MapType::vertex: { - ERR_FAIL_V_MSG((HashMap<int, T>()), "This data can't be extracted and organized per polygon, since into the FBX is mapped per vertex. This should not happen."); - } break; - case FBXDocParser::MeshGeometry::MapType::polygon_vertex: { - ERR_FAIL_V_MSG((HashMap<int, T>()), "This data can't be extracted and organized per polygon, since into the FBX is mapped per polygon vertex. This should not happen."); - } break; - case FBXDocParser::MeshGeometry::MapType::polygon: { - if (p_fbx_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index_to_direct) { - // The data is stored efficiently index_to_direct allows less data in the FBX file. - for (int polygon_index = 0; - polygon_index < polygon_count; - polygon_index += 1) { - if (p_fbx_data.index.size() == 0) { - ERR_FAIL_INDEX_V_MSG(polygon_index, (int)p_fbx_data.data.size(), (HashMap<int, T>()), "FBX file is corrupted: #ERR62"); - aggregate_polygon_data[polygon_index].push_back(p_fbx_data.data[polygon_index]); - } else { - ERR_FAIL_INDEX_V_MSG(polygon_index, (int)p_fbx_data.index.size(), (HashMap<int, T>()), "FBX file is corrupted: #ERR62"); - - const int index_to_direct = p_fbx_data.index[polygon_index]; - T value = p_fbx_data.data[index_to_direct]; - aggregate_polygon_data[polygon_index].push_back(value); - } - } - } else if (p_fbx_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::direct) { - // The data are mapped per polygon directly. - ERR_FAIL_COND_V_MSG(polygon_count != (int)p_fbx_data.data.size(), (HashMap<int, T>()), "FBX file is corrupted: #ERR51"); - - // Advance each polygon vertex, each new polygon advance the polygon index. - for (int polygon_index = 0; - polygon_index < polygon_count; - polygon_index += 1) { - ERR_FAIL_INDEX_V_MSG(polygon_index, (int)p_fbx_data.data.size(), (HashMap<int, T>()), "FBX file is corrupted: #ERR52"); - aggregate_polygon_data[polygon_index].push_back(p_fbx_data.data[polygon_index]); - } - } else { - // The data is mapped per polygon using a reference. - // The indices array, contains a *reference_id for each polygon. - // * Note that the reference_id is the id of data into the data array. - // - // https://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_layer_element_html - ERR_FAIL_COND_V_MSG(polygon_count != (int)p_fbx_data.index.size(), (HashMap<int, T>()), "FBX file seems corrupted: #ERR52"); - - // Advance each polygon vertex, each new polygon advance the polygon index. - for (int polygon_index = 0; - polygon_index < polygon_count; - polygon_index += 1) { - ERR_FAIL_INDEX_V_MSG(polygon_index, (int)p_fbx_data.index.size(), (HashMap<int, T>()), "FBX file is corrupted: #ERR53"); - ERR_FAIL_INDEX_V_MSG(p_fbx_data.index[polygon_index], (int)p_fbx_data.data.size(), (HashMap<int, T>()), "FBX file is corrupted: #ERR54"); - aggregate_polygon_data[polygon_index].push_back(p_fbx_data.data[p_fbx_data.index[polygon_index]]); - } - } - } break; - case FBXDocParser::MeshGeometry::MapType::edge: { - ERR_FAIL_V_MSG((HashMap<int, T>()), "This data can't be extracted and organized per polygon, since into the FBX is mapped per edge. This should not happen."); - } break; - case FBXDocParser::MeshGeometry::MapType::all_the_same: { - // No matter the mode, no matter the data size; The first always win - // and is set to all the vertices. - ERR_FAIL_COND_V_MSG(p_fbx_data.data.size() <= 0, (HashMap<int, T>()), "FBX file seems corrupted: #ERR55"); - if (p_fbx_data.data.size() > 0) { - for (int polygon_index = 0; polygon_index < polygon_count; polygon_index += 1) { - aggregate_polygon_data[polygon_index].push_back(p_fbx_data.data[0]); - } - } - } break; - } - - if (aggregate_polygon_data.size() == 0) { - return (HashMap<int, T>()); - } - - // A map is used because turns out that the some FBX file are not well organized - // with vertices well compacted. Using a map allows avoid those issues. - HashMap<int, T> polygons; - - // Take the first value for each vertex. - for (const Vertex *index = aggregate_polygon_data.next(nullptr); index != nullptr; index = aggregate_polygon_data.next(index)) { - Vector<T> *aggregated_polygon = aggregate_polygon_data.getptr(*index); - // This can't be null because we are just iterating. - CRASH_COND(aggregated_polygon == nullptr); - - ERR_FAIL_INDEX_V_MSG(0, (int)aggregated_polygon->size(), (HashMap<int, T>()), "The FBX file is corrupted, No valid data for this polygon index."); - - // Validate the final value. - polygons[*index] = (*aggregated_polygon)[0]; - } - - // Sanitize the data now, if the file is broken we can try import it anyway. - bool problem_found = false; - for (int polygon_i = 0; polygon_i < polygon_count; polygon_i += 1) { - if (polygons.has(polygon_i) == false) { - polygons[polygon_i] = p_fallback_value; - problem_found = true; - } - } - if (problem_found) { - WARN_PRINT("Some data is missing, this FBX file may be corrupted: #WARN1."); - } - - return polygons; -} - -void FBXMeshData::extract_morphs(const FBXDocParser::MeshGeometry *mesh_geometry, HashMap<String, MorphVertexData> &r_data) { - r_data.clear(); - - const int vertex_count = mesh_geometry->get_vertices().size(); - - for (const FBXDocParser::BlendShape *blend_shape : mesh_geometry->get_blend_shapes()) { - for (const FBXDocParser::BlendShapeChannel *blend_shape_channel : blend_shape->BlendShapeChannels()) { - const std::vector<const FBXDocParser::ShapeGeometry *> &shape_geometries = blend_shape_channel->GetShapeGeometries(); - for (const FBXDocParser::ShapeGeometry *shape_geometry : shape_geometries) { - String morph_name = ImportUtils::FBXAnimMeshName(shape_geometry->Name()).c_str(); - if (morph_name.is_empty()) { - morph_name = "morph"; - } - - // TODO we have only these?? - const std::vector<unsigned int> &morphs_vertex_indices = shape_geometry->GetIndices(); - const std::vector<Vector3> &morphs_vertices = shape_geometry->GetVertices(); - const std::vector<Vector3> &morphs_normals = shape_geometry->GetNormals(); - - ERR_FAIL_COND_MSG((int)morphs_vertex_indices.size() > vertex_count, "The FBX file is corrupted: #ERR103"); - ERR_FAIL_COND_MSG(morphs_vertex_indices.size() != morphs_vertices.size(), "The FBX file is corrupted: #ERR104"); - ERR_FAIL_COND_MSG((int)morphs_vertices.size() > vertex_count, "The FBX file is corrupted: #ERR105"); - ERR_FAIL_COND_MSG(morphs_normals.size() != 0 && morphs_normals.size() != morphs_vertices.size(), "The FBX file is corrupted: #ERR106"); - - if (r_data.has(morph_name) == false) { - // This morph doesn't exist yet. - // Create it. - MorphVertexData md; - md.vertices.resize(vertex_count); - md.normals.resize(vertex_count); - r_data.set(morph_name, md); - } - - MorphVertexData *data = r_data.getptr(morph_name); - Vector3 *data_vertices_ptr = data->vertices.ptrw(); - Vector3 *data_normals_ptr = data->normals.ptrw(); - - for (int i = 0; i < (int)morphs_vertex_indices.size(); i += 1) { - const Vertex vertex = morphs_vertex_indices[i]; - - ERR_FAIL_INDEX_MSG(vertex, vertex_count, "The blend shapes of this FBX file are corrupted. It has a not valid vertex."); - - data_vertices_ptr[vertex] = morphs_vertices[i]; - - if (morphs_normals.size() != 0) { - data_normals_ptr[vertex] = morphs_normals[i]; - } - } - } - } - } -} diff --git a/modules/fbx/data/fbx_mesh_data.h b/modules/fbx/data/fbx_mesh_data.h deleted file mode 100644 index eec7f38cd6..0000000000 --- a/modules/fbx/data/fbx_mesh_data.h +++ /dev/null @@ -1,200 +0,0 @@ -/*************************************************************************/ -/* fbx_mesh_data.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef FBX_MESH_DATA_H -#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 "scene/3d/importer_mesh_instance_3d.h" -#include "scene/3d/mesh_instance_3d.h" -#include "scene/resources/surface_tool.h" - -#include "fbx_bone.h" -#include "fbx_parser/FBXMeshGeometry.h" -#include "import_state.h" -#include "tools/import_utils.h" - -struct FBXNode; -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<float> weights; - Vector<int> bones; - // This extra vector is used because the bone id is computed in a second step. - // TODO Get rid of this extra step is a good idea. - Vector<Ref<FBXBone>> bones_ref; -}; - -template <class T> -struct VertexData { - int polygon_index; - T data; -}; - -// Caches mesh information and instantiates meshes for you using helper functions. -struct FBXMeshData : RefCounted { - struct MorphVertexData { - // TODO we have only these?? - /// Each element is a vertex. Not supposed to be void. - Vector<Vector3> vertices; - /// Each element is a vertex. Not supposed to be void. - Vector<Vector3> normals; - }; - - // FIXME: remove this is a hack for testing only - mutable const FBXDocParser::MeshGeometry *mesh_geometry = nullptr; - - Ref<FBXNode> mesh_node = nullptr; - /// vertex id, Weight Info - /// later: perf we can use array here - HashMap<int, VertexWeightMapping> vertex_weights; - - // translate fbx mesh data from document context to FBX Mesh Geometry Context - bool valid_weight_indexes = false; - - ImporterMeshInstance3D *create_fbx_mesh(const ImportState &state, const FBXDocParser::MeshGeometry *p_mesh_geometry, const FBXDocParser::Model *model, bool use_compression); - - void gen_weight_info(Ref<SurfaceTool> st, int vertex_id) const; - - /* mesh maximum weight count */ - bool valid_weight_count = false; - int max_weight_count = 0; - uint64_t armature_id = 0; - bool valid_armature_id = false; - ImporterMeshInstance3D *godot_mesh_instance = nullptr; - -private: - void sanitize_vertex_weights(const ImportState &state); - - /// Make sure to reorganize the vertices so that the correct UV is taken. - /// This step is needed because differently from the normal, that can be - /// combined, the UV may need its own triangle because sometimes they have - /// really different UV for the same vertex but different polygon. - /// This function make sure to add another vertex for those UVS. - void reorganize_vertices( - std::vector<int> &r_polygon_indices, - std::vector<Vector3> &r_vertices, - HashMap<int, Vector3> &r_normals, - HashMap<int, Vector2> &r_uv_1, - HashMap<int, Vector2> &r_uv_2, - HashMap<int, Color> &r_color, - HashMap<String, MorphVertexData> &r_morphs, - HashMap<int, HashMap<int, Vector3>> &r_normals_raw, - HashMap<int, HashMap<int, Color>> &r_colors_raw, - HashMap<int, HashMap<int, Vector2>> &r_uv_1_raw, - HashMap<int, HashMap<int, Vector2>> &r_uv_2_raw); - - void add_vertex( - const ImportState &state, - Ref<SurfaceTool> p_surface_tool, - real_t p_scale, - int p_vertex, - const std::vector<Vector3> &p_vertices_position, - const HashMap<int, Vector3> &p_normals, - const HashMap<int, Vector2> &p_uvs_0, - const HashMap<int, Vector2> &p_uvs_1, - const HashMap<int, Color> &p_colors, - const Vector3 &p_morph_value = Vector3(), - const Vector3 &p_morph_normal = Vector3()); - - 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. - /// The polygon vertices are stored in an array with some negative - /// values. The negative values define the last face index. - /// For example the following `face_array` contains two faces, the former - /// with 3 vertices and the latter with a line: - /// [0,2,-2,3,-5] - /// Parsed as: - /// [0, 2, 1, 3, 4] - /// The negative values are computed using this formula: `(-value) - 1` - /// - /// Returns the vertex index from the polygon vertex. - /// Returns -1 if `p_index` is invalid. - int get_vertex_from_polygon_vertex(const std::vector<int> &p_face_indices, int p_index) const; - - /// Returns true if this polygon_vertex_index is the end of a new polygon. - bool is_end_of_polygon(const std::vector<int> &p_face_indices, int p_index) const; - - /// Returns true if this polygon_vertex_index is the begin of a new polygon. - bool is_start_of_polygon(const std::vector<int> &p_face_indices, int p_index) const; - - /// Returns the number of polygons. - int count_polygons(const std::vector<int> &p_face_indices) const; - - /// Used to extract data from the `MappingData` aligned with vertex. - /// Useful to extract normal/uvs/colors/tangents/etc... - /// If the function fails somehow, it returns an hollow vector and print an error. - template <class R, class T> - HashMap<int, R> extract_per_vertex_data( - int p_vertex_count, - const std::vector<FBXDocParser::MeshGeometry::Edge> &p_edges, - const std::vector<int> &p_mesh_indices, - const FBXDocParser::MeshGeometry::MappingData<T> &p_mapping_data, - R (*collector_function)(const Vector<VertexData<T>> *p_vertex_data, R p_fall_back), - R p_fall_back) const; - - /// Used to extract data from the `MappingData` organized per polygon. - /// Useful to extract the material - /// If the function fails somehow, it returns an hollow vector and print an error. - template <class T> - HashMap<int, T> extract_per_polygon( - int p_vertex_count, - const std::vector<int> &p_face_indices, - const FBXDocParser::MeshGeometry::MappingData<T> &p_fbx_data, - T p_fallback_value) const; - - /// Extracts the morph data and organizes it per vertices. - /// The returned `MorphVertexData` arrays are never something different - /// then the `vertex_count`. - void extract_morphs(const FBXDocParser::MeshGeometry *mesh_geometry, HashMap<String, MorphVertexData> &r_data); -}; - -#endif // FBX_MESH_DATA_H diff --git a/modules/fbx/data/fbx_node.h b/modules/fbx/data/fbx_node.h deleted file mode 100644 index 75461e397d..0000000000 --- a/modules/fbx/data/fbx_node.h +++ /dev/null @@ -1,63 +0,0 @@ -/*************************************************************************/ -/* fbx_node.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef FBX_NODE_H -#define FBX_NODE_H - -#include "fbx_skeleton.h" -#include "model_abstraction.h" -#include "pivot_transform.h" - -#include "fbx_parser/FBXDocument.h" - -class Node3D; -struct PivotTransform; - -struct FBXNode : RefCounted, ModelAbstraction { - uint64_t current_node_id = 0; - String node_name = String(); - Node3D *godot_node = nullptr; - - // used to parent the skeleton once the tree is built. - Ref<FBXSkeleton> skeleton_node = Ref<FBXSkeleton>(); - - void set_parent(Ref<FBXNode> p_parent) { - fbx_parent = p_parent; - } - - void set_pivot_transform(Ref<PivotTransform> p_pivot_transform) { - pivot_transform = p_pivot_transform; - } - - Ref<PivotTransform> pivot_transform = Ref<PivotTransform>(); // local and global xform data - Ref<FBXNode> fbx_parent = Ref<FBXNode>(); // parent node -}; - -#endif // FBX_NODE_H diff --git a/modules/fbx/data/fbx_skeleton.cpp b/modules/fbx/data/fbx_skeleton.cpp deleted file mode 100644 index 11eed2576f..0000000000 --- a/modules/fbx/data/fbx_skeleton.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/*************************************************************************/ -/* fbx_skeleton.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "fbx_skeleton.h" - -#include "import_state.h" - -#include "tools/import_utils.h" - -void FBXSkeleton::init_skeleton(const ImportState &state) { - int skeleton_bone_count = skeleton_bones.size(); - - if (skeleton == nullptr && skeleton_bone_count > 0) { - skeleton = memnew(Skeleton3D); - - if (fbx_node.is_valid()) { - // cache skeleton attachment for later during node creation - // can't be done until after node hierarchy is built - if (fbx_node->godot_node != state.root) { - fbx_node->skeleton_node = Ref<FBXSkeleton>(this); - print_verbose("cached armature skeleton attachment for node " + fbx_node->node_name); - } else { - // root node must never be a skeleton to prevent cyclic skeletons from being allowed (skeleton in a skeleton) - fbx_node->godot_node->add_child(skeleton); - skeleton->set_owner(state.root_owner); - skeleton->set_name("Skeleton3D"); - print_verbose("created armature skeleton for root"); - } - } else { - memfree(skeleton); - skeleton = nullptr; - print_error("[doc] skeleton has no valid node to parent nodes to - erasing"); - skeleton_bones.clear(); - return; - } - } - - // Make the bone name uniques. - for (int x = 0; x < skeleton_bone_count; x++) { - Ref<FBXBone> bone = skeleton_bones[x]; - if (bone.is_valid()) { - // Make sure the bone name is unique. - const String bone_name = bone->bone_name; - int same_name_count = 0; - for (int y = x + 1; y < skeleton_bone_count; y++) { - Ref<FBXBone> other_bone = skeleton_bones[y]; - if (other_bone.is_valid()) { - if (other_bone->bone_name == bone_name) { - same_name_count += 1; - other_bone->bone_name += "_" + itos(same_name_count); - } - } - } - } - } - - Map<int, Ref<FBXBone>> bone_map; - // implement fbx cluster skin logic here this is where it goes - int bone_count = 0; - for (int x = 0; x < skeleton_bone_count; x++) { - Ref<FBXBone> bone = skeleton_bones[x]; - if (bone.is_valid()) { - skeleton->add_bone(bone->bone_name); - bone->godot_bone_id = bone_count; - bone->fbx_skeleton = Ref<FBXSkeleton>(this); - bone_map.insert(bone_count, bone); - print_verbose("added bone " + itos(bone->bone_id) + " " + bone->bone_name); - bone_count++; - } - } - - ERR_FAIL_COND_MSG(skeleton->get_bone_count() != bone_count, "Not all bones got added, is the file corrupted?"); - - for (const KeyValue<int, Ref<FBXBone>> &bone_element : bone_map) { - const Ref<FBXBone> bone = bone_element.value; - int bone_index = bone_element.key; - print_verbose("working on bone: " + itos(bone_index) + " bone name:" + bone->bone_name); - - skeleton->set_bone_rest(bone->godot_bone_id, get_unscaled_transform(bone->node->pivot_transform->LocalTransform, state.scale)); - { - Transform3D base_xform = bone->node->pivot_transform->LocalTransform; - - skeleton->set_bone_pose_position(bone_index, base_xform.origin); - skeleton->set_bone_pose_rotation(bone_index, base_xform.basis.get_rotation_quaternion()); - skeleton->set_bone_pose_scale(bone_index, base_xform.basis.get_scale()); - } - - // lookup parent ID - if (bone->valid_parent && state.fbx_bone_map.has(bone->parent_bone_id)) { - Ref<FBXBone> parent_bone = state.fbx_bone_map[bone->parent_bone_id]; - int bone_id = skeleton->find_bone(parent_bone->bone_name); - if (bone_id != -1) { - skeleton->set_bone_parent(bone_index, bone_id); - } else { - print_error("invalid bone parent: " + parent_bone->bone_name); - } - } else { - if (bone->godot_bone_id != -1) { - skeleton->set_bone_parent(bone_index, -1); // no parent for this bone - } - } - } -} diff --git a/modules/fbx/data/fbx_skeleton.h b/modules/fbx/data/fbx_skeleton.h deleted file mode 100644 index b6103df949..0000000000 --- a/modules/fbx/data/fbx_skeleton.h +++ /dev/null @@ -1,53 +0,0 @@ -/*************************************************************************/ -/* fbx_skeleton.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef FBX_SKELETON_H -#define FBX_SKELETON_H - -#include "fbx_bone.h" -#include "fbx_node.h" -#include "model_abstraction.h" - -#include "core/object/ref_counted.h" -#include "scene/3d/skeleton_3d.h" - -struct FBXNode; -struct ImportState; -struct FBXBone; - -struct FBXSkeleton : RefCounted { - Ref<FBXNode> fbx_node = Ref<FBXNode>(); - Vector<Ref<FBXBone>> skeleton_bones = Vector<Ref<FBXBone>>(); - Skeleton3D *skeleton = nullptr; - - void init_skeleton(const ImportState &state); -}; - -#endif // FBX_SKELETON_H diff --git a/modules/fbx/data/import_state.h b/modules/fbx/data/import_state.h deleted file mode 100644 index 9ba60eaacf..0000000000 --- a/modules/fbx/data/import_state.h +++ /dev/null @@ -1,112 +0,0 @@ -/*************************************************************************/ -/* import_state.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef IMPORT_STATE_H -#define IMPORT_STATE_H - -#include "fbx_mesh_data.h" -#include "tools/import_utils.h" -#include "tools/validation_tools.h" - -#include "pivot_transform.h" - -#include "core/io/resource_importer.h" -#include "core/templates/vector.h" -#include "editor/import/resource_importer_scene.h" -#include "editor/project_settings_editor.h" -#include "scene/3d/mesh_instance_3d.h" -#include "scene/3d/node_3d.h" -#include "scene/3d/skeleton_3d.h" -#include "scene/animation/animation_player.h" -#include "scene/resources/animation.h" -#include "scene/resources/surface_tool.h" - -#include "modules/fbx/fbx_parser/FBXDocument.h" -#include "modules/fbx/fbx_parser/FBXImportSettings.h" -#include "modules/fbx/fbx_parser/FBXMeshGeometry.h" -#include "modules/fbx/fbx_parser/FBXParser.h" -#include "modules/fbx/fbx_parser/FBXTokenizer.h" -#include "modules/fbx/fbx_parser/FBXUtil.h" - -struct FBXBone; -struct FBXMeshData; -struct FBXNode; -struct FBXSkeleton; - -struct ImportState { - bool enable_material_import = true; - bool enable_animation_import = true; - bool is_blender_fbx = false; - - Map<StringName, Ref<Texture>> cached_image_searches; - Map<uint64_t, Ref<Material>> cached_materials; - - String path = String(); - Node3D *root_owner = nullptr; - Node3D *root = nullptr; - real_t scale = 0.01; - Ref<FBXNode> fbx_root_node = Ref<FBXNode>(); - // skeleton map - merged automatically when they are on the same x node in the tree so we can merge them automatically. - Map<uint64_t, Ref<FBXSkeleton>> skeleton_map = Map<uint64_t, Ref<FBXSkeleton>>(); - - // nodes on the same level get merged automatically. - //Map<uint64_t, Skeleton3D *> armature_map; - AnimationPlayer *animation_player = nullptr; - - // Generation 4 - Raw document accessing for bone/skin/joint/kLocators - // joints are not necessarily bones but must be merged into the skeleton - // (bone id), bone - Map<uint64_t, Ref<FBXBone>> fbx_bone_map = Map<uint64_t, Ref<FBXBone>>(); // this is the bone name and setup information required for joints - // this will never contain joints only bones attached to a mesh. - - // Generation 4 - Raw document for creating the nodes transforms in the scene - // this is a list of the nodes in the scene - // (id, node) - List<Ref<FBXNode>> fbx_node_list = List<Ref<FBXNode>>(); - - // All nodes which have been created in the scene - // this will not contain the root node of the scene - Map<uint64_t, Ref<FBXNode>> fbx_target_map = Map<uint64_t, Ref<FBXNode>>(); - - // mesh nodes which are created in node / mesh step - used for populating skin poses in MeshSkins - Map<uint64_t, Ref<FBXNode>> MeshNodes = Map<uint64_t, Ref<FBXNode>>(); - // mesh skin map - Map<uint64_t, Ref<Skin>> MeshSkins = Map<uint64_t, Ref<Skin>>(); - - // this is the container for the mesh weight information and eventually - // any mesh data - // but not the skin, just stuff important for rendering - // skin is applied to mesh instance so not really required to be in here yet. - // maybe later - // fbx mesh id, FBXMeshData - Map<uint64_t, Ref<FBXMeshData>> renderer_mesh_data = Map<uint64_t, Ref<FBXMeshData>>(); -}; - -#endif // IMPORT_STATE_H diff --git a/modules/fbx/data/model_abstraction.h b/modules/fbx/data/model_abstraction.h deleted file mode 100644 index 528960ab49..0000000000 --- a/modules/fbx/data/model_abstraction.h +++ /dev/null @@ -1,52 +0,0 @@ -/*************************************************************************/ -/* model_abstraction.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef MODEL_ABSTRACTION_H -#define MODEL_ABSTRACTION_H - -#include "modules/fbx/fbx_parser/FBXDocument.h" - -struct ModelAbstraction { - mutable const FBXDocParser::Model *fbx_model = nullptr; - - void set_model(const FBXDocParser::Model *p_model) { - fbx_model = p_model; - } - - bool has_model() const { - return fbx_model != nullptr; - } - - const FBXDocParser::Model *get_model() const { - return fbx_model; - } -}; - -#endif // MODEL_ABSTRACTION_H diff --git a/modules/fbx/data/pivot_transform.cpp b/modules/fbx/data/pivot_transform.cpp deleted file mode 100644 index 4cf42257a4..0000000000 --- a/modules/fbx/data/pivot_transform.cpp +++ /dev/null @@ -1,307 +0,0 @@ -/*************************************************************************/ -/* pivot_transform.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "pivot_transform.h" - -#include "tools/import_utils.h" - -void PivotTransform::ReadTransformChain() { - 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. - print_verbose("Model: " + String(fbx_model->Name().c_str()) + " Has inherit type: " + itos(fbx_model->InheritType())); - bool ok = false; - raw_pre_rotation = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "PreRotation", ok)); - if (ok) { - pre_rotation = ImportUtils::EulerToQuaternion(rot, ImportUtils::deg2rad(raw_pre_rotation)); - print_verbose("valid pre_rotation: " + raw_pre_rotation + " euler conversion: " + (pre_rotation.get_euler() * (180 / Math_PI))); - } - raw_post_rotation = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "PostRotation", ok)); - if (ok) { - post_rotation = ImportUtils::EulerToQuaternion(FBXDocParser::Model::RotOrder_EulerXYZ, ImportUtils::deg2rad(raw_post_rotation)); - print_verbose("valid post_rotation: " + raw_post_rotation + " euler conversion: " + (pre_rotation.get_euler() * (180 / Math_PI))); - } - const Vector3 &RotationPivot = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "RotationPivot", ok)); - if (ok) { - rotation_pivot = ImportUtils::FixAxisConversions(RotationPivot); - } - const Vector3 &RotationOffset = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "RotationOffset", ok)); - if (ok) { - rotation_offset = ImportUtils::FixAxisConversions(RotationOffset); - } - const Vector3 &ScalingOffset = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "ScalingOffset", ok)); - if (ok) { - scaling_offset = ImportUtils::FixAxisConversions(ScalingOffset); - } - const Vector3 &ScalingPivot = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "ScalingPivot", ok)); - if (ok) { - scaling_pivot = ImportUtils::FixAxisConversions(ScalingPivot); - } - const Vector3 &Translation = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "Lcl Translation", ok)); - if (ok) { - translation = ImportUtils::FixAxisConversions(Translation); - } - raw_rotation = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "Lcl Rotation", ok)); - if (ok) { - rotation = ImportUtils::EulerToQuaternion(rot, ImportUtils::deg2rad(raw_rotation)); - } - const Vector3 &Scaling = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "Lcl Scaling", ok)); - if (ok) { - scaling = Scaling; - } else { - scaling = Vector3(1, 1, 1); - } - const Vector3 &GeometricScaling = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "GeometricScaling", ok)); - if (ok) { - geometric_scaling = GeometricScaling; - } else { - geometric_scaling = Vector3(1, 1, 1); - } - - const Vector3 &GeometricRotation = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "GeometricRotation", ok)); - if (ok) { - geometric_rotation = ImportUtils::EulerToQuaternion(rot, ImportUtils::deg2rad(GeometricRotation)); - } else { - geometric_rotation = Quaternion(); - } - - const Vector3 &GeometricTranslation = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "GeometricTranslation", ok)); - if (ok) { - geometric_translation = ImportUtils::FixAxisConversions(GeometricTranslation); - } else { - geometric_translation = Vector3(0, 0, 0); - } - - if (geometric_rotation != Quaternion()) { - print_error("geometric rotation is unsupported!"); - //CRASH_COND(true); - } - - if (!geometric_scaling.is_equal_approx(Vector3(1, 1, 1))) { - print_error("geometric scaling is unsupported!"); - //CRASH_COND(true); - } - - if (!geometric_translation.is_equal_approx(Vector3(0, 0, 0))) { - print_error("geometric translation is unsupported."); - //CRASH_COND(true); - } -} - -Transform3D PivotTransform::ComputeLocalTransform(Vector3 p_translation, Quaternion p_rotation, Vector3 p_scaling) const { - Transform3D T, Roff, Rp, Soff, Sp, S; - - // Here I assume this is the operation which needs done. - // Its WorldTransform * V - - // Origin pivots - T.set_origin(p_translation); - Roff.set_origin(rotation_offset); - Rp.set_origin(rotation_pivot); - Soff.set_origin(scaling_offset); - Sp.set_origin(scaling_pivot); - - // Scaling node - S.scale(p_scaling); - // Rotation pivots - Transform3D Rpre = Transform3D(pre_rotation); - Transform3D R = Transform3D(p_rotation); - Transform3D Rpost = Transform3D(post_rotation); - - return T * Roff * Rp * Rpre * R * Rpost.affine_inverse() * Rp.affine_inverse() * Soff * Sp * S * Sp.affine_inverse(); -} - -Transform3D PivotTransform::ComputeGlobalTransform(Transform3D t) const { - Vector3 pos = t.origin; - Vector3 scale = t.basis.get_scale(); - Quaternion rot = t.basis.get_rotation_quaternion(); - return ComputeGlobalTransform(pos, rot, scale); -} - -Transform3D PivotTransform::ComputeLocalTransform(Transform3D t) const { - Vector3 pos = t.origin; - Vector3 scale = t.basis.get_scale(); - Quaternion rot = t.basis.get_rotation_quaternion(); - return ComputeLocalTransform(pos, rot, scale); -} - -Transform3D PivotTransform::ComputeGlobalTransform(Vector3 p_translation, Quaternion p_rotation, Vector3 p_scaling) const { - Transform3D T, Roff, Rp, Soff, Sp, S; - - // Here I assume this is the operation which needs done. - // Its WorldTransform * V - - // Origin pivots - T.set_origin(p_translation); - Roff.set_origin(rotation_offset); - Rp.set_origin(rotation_pivot); - Soff.set_origin(scaling_offset); - Sp.set_origin(scaling_pivot); - - // Scaling node - S.scale(p_scaling); - - // Rotation pivots - Transform3D Rpre = Transform3D(pre_rotation); - Transform3D R = Transform3D(p_rotation); - Transform3D Rpost = Transform3D(post_rotation); - - Transform3D parent_global_xform; - Transform3D parent_local_scaling_m; - - if (parent_transform.is_valid()) { - parent_global_xform = parent_transform->GlobalTransform; - parent_local_scaling_m = parent_transform->Local_Scaling_Matrix; - } - - Transform3D local_rotation_m, parent_global_rotation_m; - Quaternion parent_global_rotation = parent_global_xform.basis.get_rotation_quaternion(); - parent_global_rotation_m.basis.set_quaternion(parent_global_rotation); - local_rotation_m = Rpre * R * Rpost; - - //Basis parent_global_rotation = Basis(parent_global_xform.get_basis().get_rotation_quaternion().normalized()); - - Transform3D local_shear_scaling, parent_shear_scaling, parent_shear_rotation, parent_shear_translation; - Vector3 parent_translation = parent_global_xform.get_origin(); - parent_shear_translation.origin = parent_translation; - parent_shear_rotation = parent_shear_translation.affine_inverse() * parent_global_xform; - parent_shear_scaling = parent_global_rotation_m.affine_inverse() * parent_shear_rotation; - local_shear_scaling = S; - - // Inherit type handler - we don't care about T here, just reordering RSrs etc. - Transform3D global_rotation_scale; - if (inherit_type == FBXDocParser::Transform_RrSs) { - global_rotation_scale = parent_global_rotation_m * local_rotation_m * parent_shear_scaling * local_shear_scaling; - } else if (inherit_type == FBXDocParser::Transform_RSrs) { - global_rotation_scale = parent_global_rotation_m * parent_shear_scaling * local_rotation_m * local_shear_scaling; - } else if (inherit_type == FBXDocParser::Transform_Rrs) { - Transform3D parent_global_shear_m_noLocal = parent_shear_scaling * parent_local_scaling_m.affine_inverse(); - global_rotation_scale = parent_global_rotation_m * local_rotation_m * parent_global_shear_m_noLocal * local_shear_scaling; - } - Transform3D local_transform = T * Roff * Rp * Rpre * R * Rpost.affine_inverse() * Rp.affine_inverse() * Soff * Sp * S * Sp.affine_inverse(); - //Transform3D local_translation_pivoted = Transform3D(Basis(), LocalTransform.origin); - - ERR_FAIL_COND_V_MSG(local_transform.basis.determinant() == 0, Transform3D(), "Det == 0 prevented in scene file"); - - // manual hack to force SSC not to be compensated for - until we can handle it properly with tests - return parent_global_xform * local_transform; -} - -void PivotTransform::ComputePivotTransform() { - Transform3D T, Roff, Rp, Soff, Sp, S; - - // Here I assume this is the operation which needs done. - // Its WorldTransform * V - - // Origin pivots - T.set_origin(translation); - Roff.set_origin(rotation_offset); - Rp.set_origin(rotation_pivot); - Soff.set_origin(scaling_offset); - Sp.set_origin(scaling_pivot); - - // Scaling node - if (!scaling.is_equal_approx(Vector3())) { - S.scale(scaling); - } else { - S.scale(Vector3(1, 1, 1)); - } - Local_Scaling_Matrix = S; // copy for when node / child is looking for the value of this. - - // Rotation pivots - Transform3D Rpre = Transform3D(pre_rotation); - Transform3D R = Transform3D(rotation); - Transform3D Rpost = Transform3D(post_rotation); - - Transform3D parent_global_xform; - Transform3D parent_local_scaling_m; - - if (parent_transform.is_valid()) { - parent_global_xform = parent_transform->GlobalTransform; - parent_local_scaling_m = parent_transform->Local_Scaling_Matrix; - } - - Transform3D local_rotation_m, parent_global_rotation_m; - Quaternion parent_global_rotation = parent_global_xform.basis.get_rotation_quaternion(); - parent_global_rotation_m.basis.set_quaternion(parent_global_rotation); - local_rotation_m = Rpre * R * Rpost; - - //Basis parent_global_rotation = Basis(parent_global_xform.get_basis().get_rotation_quaternion().normalized()); - - Transform3D local_shear_scaling, parent_shear_scaling, parent_shear_rotation, parent_shear_translation; - Vector3 parent_translation = parent_global_xform.get_origin(); - parent_shear_translation.origin = parent_translation; - parent_shear_rotation = parent_shear_translation.affine_inverse() * parent_global_xform; - parent_shear_scaling = parent_global_rotation_m.affine_inverse() * parent_shear_rotation; - local_shear_scaling = S; - - // Inherit type handler - we don't care about T here, just reordering RSrs etc. - Transform3D global_rotation_scale; - if (inherit_type == FBXDocParser::Transform_RrSs) { - global_rotation_scale = parent_global_rotation_m * local_rotation_m * parent_shear_scaling * local_shear_scaling; - } else if (inherit_type == FBXDocParser::Transform_RSrs) { - global_rotation_scale = parent_global_rotation_m * parent_shear_scaling * local_rotation_m * local_shear_scaling; - } else if (inherit_type == FBXDocParser::Transform_Rrs) { - Transform3D parent_global_shear_m_noLocal = parent_shear_scaling * parent_local_scaling_m.inverse(); - global_rotation_scale = parent_global_rotation_m * local_rotation_m * parent_global_shear_m_noLocal * local_shear_scaling; - } - LocalTransform = Transform3D(); - LocalTransform = T * Roff * Rp * Rpre * R * Rpost.affine_inverse() * Rp.affine_inverse() * Soff * Sp * S * Sp.affine_inverse(); - - ERR_FAIL_COND_MSG(LocalTransform.basis.determinant() == 0, "invalid scale reset"); - - Transform3D local_translation_pivoted = Transform3D(Basis(), LocalTransform.origin); - GlobalTransform = Transform3D(); - //GlobalTransform = parent_global_xform * LocalTransform; - Transform3D global_origin = Transform3D(Basis(), parent_translation); - GlobalTransform = (global_origin * local_translation_pivoted) * global_rotation_scale; - - ImportUtils::debug_xform("local xform calculation", LocalTransform); - print_verbose("scale of node: " + S.basis.get_scale_local()); - print_verbose("---------------------------------------------------------------"); -} - -void PivotTransform::Execute() { - ReadTransformChain(); - ComputePivotTransform(); - - ImportUtils::debug_xform("global xform: ", GlobalTransform); - - if (LocalTransform.basis.determinant() == 0) { - print_error("Serious det == 0!"); - } - - if (GlobalTransform.basis.determinant() == 0) { - print_error("Serious! node has det == 0!"); - } - - computed_global_xform = true; -} diff --git a/modules/fbx/data/pivot_transform.h b/modules/fbx/data/pivot_transform.h deleted file mode 100644 index 099b268075..0000000000 --- a/modules/fbx/data/pivot_transform.h +++ /dev/null @@ -1,115 +0,0 @@ -/*************************************************************************/ -/* pivot_transform.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef PIVOT_TRANSFORM_H -#define PIVOT_TRANSFORM_H - -#include "core/math/transform_3d.h" -#include "core/object/ref_counted.h" - -#include "model_abstraction.h" - -#include "fbx_parser/FBXDocument.h" -#include "tools/import_utils.h" - -enum TransformationComp { - TransformationComp_Translation, - TransformationComp_Scaling, - TransformationComp_Rotation, - TransformationComp_RotationOffset, - TransformationComp_RotationPivot, - TransformationComp_PreRotation, - TransformationComp_PostRotation, - TransformationComp_ScalingOffset, - TransformationComp_ScalingPivot, - TransformationComp_GeometricTranslation, - TransformationComp_GeometricRotation, - TransformationComp_GeometricScaling, - TransformationComp_MAXIMUM -}; -// Abstract away pivot data so its simpler to handle -struct PivotTransform : RefCounted, ModelAbstraction { - // at the end we want to keep geometric_ everything, post and pre rotation - // these are used during animation data processing / keyframe ingestion the rest can be simplified down / out. - Quaternion pre_rotation = Quaternion(); - Quaternion post_rotation = Quaternion(); - Quaternion rotation = Quaternion(); - Quaternion geometric_rotation = Quaternion(); - Vector3 rotation_pivot = Vector3(); - Vector3 rotation_offset = Vector3(); - Vector3 scaling_offset = Vector3(1.0, 1.0, 1.0); - Vector3 scaling_pivot = Vector3(1.0, 1.0, 1.0); - Vector3 translation = Vector3(); - Vector3 scaling = Vector3(1.0, 1.0, 1.0); - Vector3 geometric_scaling = Vector3(1.0, 1.0, 1.0); - Vector3 geometric_translation = Vector3(); - - Vector3 raw_rotation = Vector3(); - Vector3 raw_post_rotation = Vector3(); - Vector3 raw_pre_rotation = Vector3(); - - /* Read pivots from the document */ - void ReadTransformChain(); - - void debug_pivot_xform(String p_name) { - print_verbose("debugging node name: " + p_name); - print_verbose("raw rotation: " + raw_rotation * (180 / Math_PI)); - print_verbose("raw pre_rotation " + raw_pre_rotation * (180 / Math_PI)); - print_verbose("raw post_rotation " + raw_post_rotation * (180 / Math_PI)); - } - - Transform3D ComputeGlobalTransform(Transform3D t) const; - Transform3D ComputeLocalTransform(Transform3D t) const; - Transform3D ComputeGlobalTransform(Vector3 p_translation, Quaternion p_rotation, Vector3 p_scaling) const; - Transform3D ComputeLocalTransform(Vector3 p_translation, Quaternion p_rotation, Vector3 p_scaling) const; - - /* Extract into xforms and calculate once */ - void ComputePivotTransform(); - - /* Execute the command for the pivot generation */ - void Execute(); - - void set_parent(Ref<PivotTransform> p_parent) { - parent_transform = p_parent; - } - - bool computed_global_xform = false; - Ref<PivotTransform> parent_transform = Ref<PivotTransform>(); - //Transform chain[TransformationComp_MAXIMUM]; - - // cached for later use - Transform3D GlobalTransform = Transform3D(); - Transform3D LocalTransform = Transform3D(); - Transform3D Local_Scaling_Matrix = Transform3D(); // used for inherit type. - Transform3D GeometricTransform = Transform3D(); // 3DS max only - FBXDocParser::TransformInheritance inherit_type = FBXDocParser::TransformInheritance_MAX; // maya fbx requires this - sorry <3 -}; - -#endif // PIVOT_TRANSFORM_H diff --git a/modules/fbx/editor_scene_importer_fbx.cpp b/modules/fbx/editor_scene_importer_fbx.cpp deleted file mode 100644 index b11c145599..0000000000 --- a/modules/fbx/editor_scene_importer_fbx.cpp +++ /dev/null @@ -1,1470 +0,0 @@ -/*************************************************************************/ -/* editor_scene_importer_fbx.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "editor_scene_importer_fbx.h" - -#include "data/fbx_anim_container.h" -#include "data/fbx_material.h" -#include "data/fbx_mesh_data.h" -#include "data/fbx_skeleton.h" -#include "tools/import_utils.h" - -#include "core/io/image_loader.h" -#include "editor/editor_log.h" -#include "editor/editor_node.h" -#include "editor/import/resource_importer_scene.h" -#include "scene/3d/bone_attachment_3d.h" -#include "scene/3d/camera_3d.h" -#include "scene/3d/importer_mesh_instance_3d.h" -#include "scene/3d/light_3d.h" -#include "scene/main/node.h" -#include "scene/resources/material.h" - -#include "fbx_parser/FBXDocument.h" -#include "fbx_parser/FBXImportSettings.h" -#include "fbx_parser/FBXMeshGeometry.h" -#include "fbx_parser/FBXParser.h" -#include "fbx_parser/FBXProperties.h" -#include "fbx_parser/FBXTokenizer.h" - -#include <string> - -void EditorSceneFormatImporterFBX::get_extensions(List<String> *r_extensions) const { - // register FBX as the one and only format for FBX importing - const String import_setting_string = "filesystem/import/fbx/"; - const String fbx_str = "fbx"; - Vector<String> exts; - exts.push_back(fbx_str); - _register_project_setting_import(fbx_str, import_setting_string, exts, r_extensions, true); -} - -void EditorSceneFormatImporterFBX::_register_project_setting_import(const String generic, - const String import_setting_string, - const Vector<String> &exts, - List<String> *r_extensions, - const bool p_enabled) const { - const String use_generic = "use_" + generic; - _GLOBAL_DEF(import_setting_string + use_generic, p_enabled, true); - if (ProjectSettings::get_singleton()->get(import_setting_string + use_generic)) { - for (int32_t i = 0; i < exts.size(); i++) { - r_extensions->push_back(exts[i]); - } - } -} - -uint32_t EditorSceneFormatImporterFBX::get_import_flags() const { - return IMPORT_SCENE; -} - -Node3D *EditorSceneFormatImporterFBX::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, - List<String> *r_missing_deps, Error *r_err) { - // done for performance when re-importing lots of files when testing importer in verbose only! - if (OS::get_singleton()->is_stdout_verbose()) { - EditorLog *log = EditorNode::get_log(); - log->clear(); - } - Error err; - FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err); - - ERR_FAIL_COND_V(!f, nullptr); - - { - PackedByteArray data; - // broadphase tokenizing pass in which we identify the core - // syntax elements of FBX (brackets, commas, key:value mappings) - FBXDocParser::TokenList tokens; - - bool is_binary = false; - data.resize(f->get_length()); - - ERR_FAIL_COND_V(data.size() < 64, nullptr); - - f->get_buffer(data.ptrw(), data.size()); - PackedByteArray fbx_header; - fbx_header.resize(64); - for (int32_t byte_i = 0; byte_i < 64; byte_i++) { - fbx_header.ptrw()[byte_i] = data.ptr()[byte_i]; - } - - String fbx_header_string; - if (fbx_header.size() >= 0) { - fbx_header_string.parse_utf8((const char *)fbx_header.ptr(), fbx_header.size()); - } - - 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(), corrupt); - - } else { - print_verbose("[doc] is ascii"); - 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: - // 1. Tokens are made, these are then taken into the 'parser' below - // 2. The parser constructs 'Elements' and all 'real' FBX Types. - // 3. This creates a problem: shared_ptr ownership, should Elements later 'take ownership' - // 4. No, it shouldn't so we should either a.) use weak ref for elements; but this is not correct. - - // 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; - - // this function leaks a lot - FBXDocParser::Document doc(parser, settings); - - // yeah so closing the file is a good idea (prevents readonly states) - f->close(); - - // safety for version handling - if (doc.IsSafeToImport()) { - bool is_blender_fbx = false; - 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); - if (app_name_string) { - print_verbose("FBX App Name: " + String(app_name_string->Value().c_str())); - } - } - - if (app_vendor) { - const FBXDocParser::TypedProperty<std::string> *app_vendor_string = dynamic_cast<const FBXDocParser::TypedProperty<std::string> *>(app_vendor); - if (app_vendor_string) { - print_verbose("FBX App Vendor: " + String(app_vendor_string->Value().c_str())); - is_blender_fbx = app_vendor_string->Value().find("Blender") != std::string::npos; - } - } - - if (app_version) { - const FBXDocParser::TypedProperty<std::string> *app_version_string = dynamic_cast<const FBXDocParser::TypedProperty<std::string> *>(app_version); - if (app_version_string) { - print_verbose("FBX App Version: " + String(app_version_string->Value().c_str())); - } - } - - if (is_blender_fbx) { - WARN_PRINT("We don't officially support Blender FBX animations yet, due to issues with upstream Blender,\n" - "so please wait for us to work around remaining issues. We will continue to import the file but it may be broken.\n" - "For minimal breakage, please export FBX from Blender with -Z forward, and Y up."); - } - - Node3D *spatial = _generate_scene(p_path, &doc, p_flags, p_bake_fps, 8, is_blender_fbx); - // todo: move to document shutdown (will need to be validated after moving; this code has been validated already) - for (FBXDocParser::TokenPtr token : tokens) { - if (token) { - delete token; - token = nullptr; - } - } - - 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())); - } - } - - return memnew(Node3D); -} - -template <class T> -struct EditorSceneFormatImporterAssetImportInterpolate { - T lerp(const T &a, const T &b, float c) const { - return a + (b - a) * c; - } - - T catmull_rom(const T &p0, const T &p1, const T &p2, const T &p3, float t) { - const float t2 = t * t; - const float t3 = t2 * t; - - return 0.5f * ((2.0f * p1) + (-p0 + p2) * t + (2.0f * p0 - 5.0f * p1 + 4.0f * p2 - p3) * t2 + (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3); - } - - T bezier(T start, T control_1, T control_2, T end, float t) { - /* Formula from Wikipedia article on Bezier curves. */ - const real_t omt = (1.0 - t); - const real_t omt2 = omt * omt; - const real_t omt3 = omt2 * omt; - const real_t t2 = t * t; - const real_t t3 = t2 * t; - - return start * omt3 + control_1 * omt2 * t * 3.0 + control_2 * omt * t2 * 3.0 + end * t3; - } -}; - -//thank you for existing, partial specialization -template <> -struct EditorSceneFormatImporterAssetImportInterpolate<Quaternion> { - Quaternion lerp(const Quaternion &a, const Quaternion &b, float c) const { - ERR_FAIL_COND_V(!a.is_normalized(), Quaternion()); - ERR_FAIL_COND_V(!b.is_normalized(), Quaternion()); - - return a.slerp(b, c).normalized(); - } - - Quaternion catmull_rom(const Quaternion &p0, const Quaternion &p1, const Quaternion &p2, const Quaternion &p3, float c) { - ERR_FAIL_COND_V(!p1.is_normalized(), Quaternion()); - ERR_FAIL_COND_V(!p2.is_normalized(), Quaternion()); - - return p1.slerp(p2, c).normalized(); - } - - Quaternion bezier(Quaternion start, Quaternion control_1, Quaternion control_2, Quaternion end, float t) { - ERR_FAIL_COND_V(!start.is_normalized(), Quaternion()); - ERR_FAIL_COND_V(!end.is_normalized(), Quaternion()); - - return start.slerp(end, t).normalized(); - } -}; - -template <class T> -T EditorSceneFormatImporterFBX::_interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time, - AssetImportAnimation::Interpolation p_interp) { - //could use binary search, worth it? - int idx = -1; - for (int i = 0; i < p_times.size(); i++) { - if (p_times[i] > p_time) { - break; - } - idx++; - } - - EditorSceneFormatImporterAssetImportInterpolate<T> interp; - - switch (p_interp) { - case AssetImportAnimation::INTERP_LINEAR: { - if (idx == -1) { - return p_values[0]; - } else if (idx >= p_times.size() - 1) { - return p_values[p_times.size() - 1]; - } - - float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]); - - return interp.lerp(p_values[idx], p_values[idx + 1], c); - - } break; - case AssetImportAnimation::INTERP_STEP: { - if (idx == -1) { - return p_values[0]; - } else if (idx >= p_times.size() - 1) { - return p_values[p_times.size() - 1]; - } - - return p_values[idx]; - - } break; - case AssetImportAnimation::INTERP_CATMULLROMSPLINE: { - if (idx == -1) { - return p_values[1]; - } else if (idx >= p_times.size() - 1) { - return p_values[1 + p_times.size() - 1]; - } - - float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]); - - return interp.catmull_rom(p_values[idx - 1], p_values[idx], p_values[idx + 1], p_values[idx + 3], c); - - } break; - case AssetImportAnimation::INTERP_CUBIC_SPLINE: { - if (idx == -1) { - return p_values[1]; - } else if (idx >= p_times.size() - 1) { - return p_values[(p_times.size() - 1) * 3 + 1]; - } - - float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]); - - T from = p_values[idx * 3 + 1]; - T c1 = from + p_values[idx * 3 + 2]; - T to = p_values[idx * 3 + 4]; - T c2 = to + p_values[idx * 3 + 3]; - - return interp.bezier(from, c1, c2, to, c); - - } break; - } - - ERR_FAIL_V(p_values[0]); -} - -Node3D *EditorSceneFormatImporterFBX::_generate_scene( - const String &p_path, - const FBXDocParser::Document *p_document, - const uint32_t p_flags, - int p_bake_fps, - const int32_t p_max_bone_weights, - bool p_is_blender_fbx) { - ImportState state; - state.is_blender_fbx = p_is_blender_fbx; - state.path = p_path; - state.animation_player = nullptr; - - // create new root node for scene - Node3D *scene_root = memnew(Node3D); - state.root = memnew(Node3D); - state.root_owner = scene_root; // the real scene root... sorry compatibility code is painful... - - state.root->set_name("RootNode"); - scene_root->add_child(state.root); - state.root->set_owner(scene_root); - - state.fbx_root_node.instantiate(); - state.fbx_root_node->godot_node = state.root; - - // Size relative to cm. - const real_t fbx_unit_scale = p_document->GlobalSettingsPtr()->UnitScaleFactor(); - - print_verbose("FBX unit scale import value: " + rtos(fbx_unit_scale)); - // Set FBX file scale is relative to CM must be converted to M - state.scale = fbx_unit_scale / 100.0; - print_verbose("FBX unit scale is: " + rtos(state.scale)); - - // Enabled by default. - state.enable_material_import = true; - // Enabled by default. - state.enable_animation_import = true; - Ref<FBXNode> root_node; - root_node.instantiate(); - - // make sure fake noFBXDocParser::PropertyPtr ptrde always has a transform too ;) - Ref<PivotTransform> pivot_transform; - pivot_transform.instantiate(); - root_node->pivot_transform = pivot_transform; - root_node->node_name = "root node"; - root_node->current_node_id = 0; - root_node->godot_node = state.root; - - // cache this node onto the fbx_target map. - state.fbx_target_map.insert(0, root_node); - - // cache basic node information from FBX document - // grabs all FBX bones - BuildDocumentBones(Ref<FBXBone>(), state, p_document, 0L); - BuildDocumentNodes(Ref<PivotTransform>(), state, p_document, 0L, nullptr); - - // Build document skinning information - - // Algorithm is this: - // Get Deformer: object with "Skin" class. - // Deformer:: has link to Geometry:: (correct mesh for skin) - // Deformer:: has Source which is the SubDeformer:: (e.g. the Cluster) - // Notes at the end it configures the vertex weight mapping. - - for (uint64_t skin_id : p_document->GetSkinIDs()) { - // Validate the parser - FBXDocParser::LazyObject *lazy_skin = p_document->GetObject(skin_id); - ERR_CONTINUE_MSG(lazy_skin == nullptr, "invalid lazy object [serious parser bug]"); - - // Validate the parser - const FBXDocParser::Skin *skin = lazy_skin->Get<FBXDocParser::Skin>(); - ERR_CONTINUE_MSG(skin == nullptr, "invalid skin added to skin list [parser bug]"); - - const std::vector<const FBXDocParser::Connection *> source_to_destination = p_document->GetConnectionsBySourceSequenced(skin_id); - FBXDocParser::MeshGeometry *mesh = nullptr; - uint64_t mesh_id = 0; - - // Most likely only contains the mesh link for the skin - // The mesh geometry. - for (const FBXDocParser::Connection *con : source_to_destination) { - // do something - print_verbose("src: " + itos(con->src)); - FBXDocParser::Object *ob = con->DestinationObject(); - mesh = dynamic_cast<FBXDocParser::MeshGeometry *>(ob); - - if (mesh) { - mesh_id = mesh->ID(); - break; - } - } - - // Validate the mesh exists and was retrieved - ERR_CONTINUE_MSG(mesh_id == 0, "mesh id is invalid"); - const std::vector<const FBXDocParser::Cluster *> clusters = skin->Clusters(); - - // NOTE: this will ONLY work on skinned bones (it is by design.) - // A cluster is a skinned bone so SKINS won't contain unskinned bones so we need to pre-add all bones and parent them in a step beforehand. - for (const FBXDocParser::Cluster *cluster : clusters) { - ERR_CONTINUE_MSG(cluster == nullptr, "invalid bone cluster"); - const uint64_t deformer_id = cluster->ID(); - std::vector<const FBXDocParser::Connection *> connections = p_document->GetConnectionsByDestinationSequenced(deformer_id); - - // Weight data always has a node in the scene lets grab the limb's node in the scene :) (reverse set to true since it's the opposite way around) - const FBXDocParser::ModelLimbNode *limb_node = ProcessDOMConnection<FBXDocParser::ModelLimbNode>(p_document, deformer_id, true); - - ERR_CONTINUE_MSG(limb_node == nullptr, "unable to resolve model for skinned bone"); - - const uint64_t model_id = limb_node->ID(); - - // This will never happen, so if it does you know you fucked up. - ERR_CONTINUE_MSG(!state.fbx_bone_map.has(model_id), "missing LimbNode detected"); - - // new bone instance - Ref<FBXBone> bone_element = state.fbx_bone_map[model_id]; - - // - // Bone Weight Information Configuration - // - - // Cache Weight Information into bone for later usage if you want the raw data. - const std::vector<unsigned int> &indexes = cluster->GetIndices(); - const std::vector<float> &weights = cluster->GetWeights(); - Ref<FBXMeshData> mesh_vertex_data; - - // this data will pre-exist if vertex weight information is found - if (state.renderer_mesh_data.has(mesh_id)) { - mesh_vertex_data = state.renderer_mesh_data[mesh_id]; - } else { - mesh_vertex_data.instantiate(); - state.renderer_mesh_data.insert(mesh_id, mesh_vertex_data); - } - - mesh_vertex_data->armature_id = bone_element->armature_id; - mesh_vertex_data->valid_armature_id = true; - - //print_verbose("storing mesh vertex data for mesh to use later"); - ERR_CONTINUE_MSG(indexes.size() != weights.size(), "[doc] error mismatch between weight info"); - - for (size_t idx = 0; idx < indexes.size(); idx++) { - const size_t vertex_index = indexes[idx]; - const real_t influence_weight = weights[idx]; - - VertexWeightMapping &vm = mesh_vertex_data->vertex_weights[vertex_index]; - vm.weights.push_back(influence_weight); - vm.bones.push_back(0); // bone id is pushed on here during sanitization phase - vm.bones_ref.push_back(bone_element); - } - - for (const int *vertex_index = mesh_vertex_data->vertex_weights.next(nullptr); - vertex_index != nullptr; - vertex_index = mesh_vertex_data->vertex_weights.next(vertex_index)) { - VertexWeightMapping *vm = mesh_vertex_data->vertex_weights.getptr(*vertex_index); - const int influence_count = vm->weights.size(); - if (influence_count > mesh_vertex_data->max_weight_count) { - mesh_vertex_data->max_weight_count = influence_count; - mesh_vertex_data->valid_weight_count = true; - } - } - - if (mesh_vertex_data->max_weight_count > 4) { - if (mesh_vertex_data->max_weight_count > 8) { - ERR_PRINT("[doc] Serious: maximum bone influences is 8 in this branch."); - } - // Clamp to 8 bone vertex influences. - mesh_vertex_data->max_weight_count = 8; - print_verbose("[doc] Using 8 vertex bone influences configuration."); - } else { - mesh_vertex_data->max_weight_count = 4; - print_verbose("[doc] Using 4 vertex bone influences configuration."); - } - } - } - - // do we globally allow for import of materials - // (prevents overwrite of materials; so you can handle them explicitly) - if (state.enable_material_import) { - const std::vector<uint64_t> &materials = p_document->GetMaterialIDs(); - - for (uint64_t material_id : materials) { - FBXDocParser::LazyObject *lazy_material = p_document->GetObject(material_id); - FBXDocParser::Material *mat = (FBXDocParser::Material *)lazy_material->Get<FBXDocParser::Material>(); - ERR_CONTINUE_MSG(!mat, "Could not convert fbx material by id: " + itos(material_id)); - - Ref<FBXMaterial> material; - material.instantiate(); - material->set_imported_material(mat); - - Ref<StandardMaterial3D> godot_material = material->import_material(state); - - state.cached_materials.insert(material_id, godot_material); - } - } - - // build skin and skeleton information - print_verbose("[doc] Skeleton3D Bone count: " + itos(state.fbx_bone_map.size())); - - // Importing bones using document based method from FBX directly - // We do not use the assimp bone format to determine this information anymore. - if (state.fbx_bone_map.size() > 0) { - // We are using a single skeleton only method here - // this is because we really have no concept of skeletons in FBX - // their are bones in a scene but they have no specific armature - // we can detect armatures but the issue lies in the complexity - // we opted to merge the entire scene onto one skeleton for now - // if we need to change this we have an archive of the old code. - - // bind pose normally only has 1 per mesh but can have more than one - // this is the point of skins - // in FBX first bind pose is the master for the first skin - - // In order to handle the FBX skeleton we must also inverse any parent transforms on the bones - // just to rule out any parent node transforms in the bone data - // this is trivial to do and allows us to use the single skeleton method and merge them - // this means that the nodes from maya kLocators will be preserved as bones - // in the same rig without having to match this across skeletons and merge by detection - // we can just merge and undo any parent transforms - for (KeyValue<uint64_t, Ref<FBXBone>> &bone_element : state.fbx_bone_map) { - Ref<FBXBone> bone = bone_element.value; - Ref<FBXSkeleton> fbx_skeleton_inst; - - uint64_t armature_id = bone->armature_id; - if (state.skeleton_map.has(armature_id)) { - fbx_skeleton_inst = state.skeleton_map[armature_id]; - } else { - fbx_skeleton_inst.instantiate(); - state.skeleton_map.insert(armature_id, fbx_skeleton_inst); - } - - print_verbose("populating skeleton with bone: " + bone->bone_name); - - //// populate bone skeleton - since fbx has no DOM for the skeleton just a node. - //bone->bone_skeleton = fbx_skeleton_inst; - - // now populate bone on the armature node list - fbx_skeleton_inst->skeleton_bones.push_back(bone); - - CRASH_COND_MSG(!state.fbx_target_map.has(armature_id), "invalid armature [serious]"); - - Ref<FBXNode> node = state.fbx_target_map[armature_id]; - - CRASH_COND_MSG(node.is_null(), "invalid node [serious]"); - CRASH_COND_MSG(node->pivot_transform.is_null(), "invalid pivot transform [serious]"); - fbx_skeleton_inst->fbx_node = node; - - ERR_CONTINUE_MSG(fbx_skeleton_inst->fbx_node.is_null(), "invalid skeleton node [serious]"); - - // we need to have a valid armature id and the model configured for the bone to be assigned fully. - // happens once per skeleton - - if (state.fbx_target_map.has(armature_id) && !fbx_skeleton_inst->fbx_node->has_model()) { - print_verbose("allocated fbx skeleton primary / armature node for the level: " + fbx_skeleton_inst->fbx_node->node_name); - } else if (!state.fbx_target_map.has(armature_id) && !fbx_skeleton_inst->fbx_node->has_model()) { - print_error("bones are not mapped to an armature node for armature id: " + itos(armature_id) + " bone: " + bone->bone_name); - // this means bone will be removed and not used, which is safe actually and no skeleton will be created. - } - } - - // setup skeleton instances if required :) - for (KeyValue<uint64_t, Ref<FBXSkeleton>> &skeleton_node : state.skeleton_map) { - Ref<FBXSkeleton> &skeleton = skeleton_node.value; - skeleton->init_skeleton(state); - - ERR_CONTINUE_MSG(skeleton->fbx_node.is_null(), "invalid fbx target map, missing skeleton"); - } - - // This list is not populated - for (Map<uint64_t, Ref<FBXNode>>::Element *skin_mesh = state.MeshNodes.front(); skin_mesh; skin_mesh = skin_mesh->next()) { - } - } - - // build godot node tree - if (state.fbx_node_list.size() > 0) { - for (List<Ref<FBXNode>>::Element *node_element = state.fbx_node_list.front(); - node_element; - node_element = node_element->next()) { - Ref<FBXNode> fbx_node = node_element->get(); - ImporterMeshInstance3D *mesh_node = nullptr; - Ref<FBXMeshData> mesh_data_precached; - - // check for valid geometry - if (fbx_node->fbx_model == nullptr) { - print_error("[doc] fundamental flaw, submit bug immediately with full import log with verbose logging on"); - } else { - const std::vector<const FBXDocParser::Geometry *> &geometry = fbx_node->fbx_model->GetGeometry(); - for (const FBXDocParser::Geometry *mesh : geometry) { - print_verbose("[doc] [" + itos(mesh->ID()) + "] mesh: " + fbx_node->node_name); - - if (mesh == nullptr) { - continue; - } - - const FBXDocParser::MeshGeometry *mesh_geometry = dynamic_cast<const FBXDocParser::MeshGeometry *>(mesh); - if (mesh_geometry) { - uint64_t mesh_id = mesh_geometry->ID(); - - // this data will pre-exist if vertex weight information is found - if (state.renderer_mesh_data.has(mesh_id)) { - mesh_data_precached = state.renderer_mesh_data[mesh_id]; - } else { - mesh_data_precached.instantiate(); - state.renderer_mesh_data.insert(mesh_id, mesh_data_precached); - } - - mesh_data_precached->mesh_node = fbx_node; - - // mesh node, mesh id - mesh_node = mesh_data_precached->create_fbx_mesh(state, mesh_geometry, fbx_node->fbx_model, false); - if (!state.MeshNodes.has(mesh_id)) { - state.MeshNodes.insert(mesh_id, fbx_node); - } - } - - const FBXDocParser::ShapeGeometry *shape_geometry = dynamic_cast<const FBXDocParser::ShapeGeometry *>(mesh); - if (shape_geometry != nullptr) { - print_verbose("[doc] valid shape geometry converted"); - } - } - } - - Ref<FBXSkeleton> node_skeleton = fbx_node->skeleton_node; - - if (node_skeleton.is_valid()) { - Skeleton3D *skel = node_skeleton->skeleton; - fbx_node->godot_node = skel; - } else if (mesh_node == nullptr) { - fbx_node->godot_node = memnew(Node3D); - } else { - fbx_node->godot_node = mesh_node; - } - - fbx_node->godot_node->set_name(fbx_node->node_name); - - // assign parent if valid - if (fbx_node->fbx_parent.is_valid()) { - fbx_node->fbx_parent->godot_node->add_child(fbx_node->godot_node); - fbx_node->godot_node->set_owner(state.root_owner); - } - - // Node Transform debug, set local xform data. - fbx_node->godot_node->set_transform(get_unscaled_transform(fbx_node->pivot_transform->LocalTransform, state.scale)); - - // populate our mesh node reference - if (mesh_node != nullptr && mesh_data_precached.is_valid()) { - mesh_data_precached->godot_mesh_instance = mesh_node; - } - } - } - - for (KeyValue<uint64_t, Ref<FBXMeshData>> &mesh_data : state.renderer_mesh_data) { - const uint64_t mesh_id = mesh_data.key; - Ref<FBXMeshData> mesh = mesh_data.value; - - const FBXDocParser::MeshGeometry *mesh_geometry = p_document->GetObject(mesh_id)->Get<FBXDocParser::MeshGeometry>(); - - ERR_CONTINUE_MSG(mesh->mesh_node.is_null(), "invalid mesh allocation"); - - const FBXDocParser::Skin *mesh_skin = mesh_geometry->DeformerSkin(); - - if (!mesh_skin) { - continue; // safe to continue - } - - // - // Skin bone configuration - // - - // - // Get Mesh Node Xform only - // - //ERR_CONTINUE_MSG(!state.fbx_target_map.has(mesh_id), "invalid xform for the skin pose: " + itos(mesh_id)); - //Ref<FBXNode> mesh_node_xform_data = state.fbx_target_map[mesh_id]; - - if (!mesh_skin) { - continue; // not a deformer. - } - - if (mesh_skin->Clusters().size() == 0) { - continue; // possibly buggy mesh - } - - // Lookup skin or create it if it's not found. - Ref<Skin> skin; - if (!state.MeshSkins.has(mesh_id)) { - print_verbose("Created new skin"); - skin.instantiate(); - state.MeshSkins.insert(mesh_id, skin); - } else { - print_verbose("Grabbed skin"); - skin = state.MeshSkins[mesh_id]; - } - - for (const FBXDocParser::Cluster *cluster : mesh_skin->Clusters()) { - // node or bone this cluster targets (in theory will only be a bone target) - uint64_t skin_target_id = cluster->TargetNode()->ID(); - - print_verbose("adding cluster [" + itos(cluster->ID()) + "] " + String(cluster->Name().c_str()) + " for target: [" + itos(skin_target_id) + "] " + String(cluster->TargetNode()->Name().c_str())); - ERR_CONTINUE_MSG(!state.fbx_bone_map.has(skin_target_id), "no bone found by that ID? locator"); - - const Ref<FBXBone> bone = state.fbx_bone_map[skin_target_id]; - const Ref<FBXSkeleton> skeleton = bone->fbx_skeleton; - const Ref<FBXNode> skeleton_node = skeleton->fbx_node; - - skin->add_named_bind( - bone->bone_name, - get_unscaled_transform( - skeleton_node->pivot_transform->GlobalTransform.affine_inverse() * cluster->TransformLink().affine_inverse(), state.scale)); - } - - print_verbose("cluster name / id: " + String(mesh_skin->Name().c_str()) + " [" + itos(mesh_skin->ID()) + "]"); - print_verbose("skeleton has " + itos(state.fbx_bone_map.size()) + " binds"); - print_verbose("fbx skin has " + itos(mesh_skin->Clusters().size()) + " binds"); - } - - // mesh data iteration for populating skeleton mapping - for (KeyValue<uint64_t, Ref<FBXMeshData>> &mesh_data : state.renderer_mesh_data) { - Ref<FBXMeshData> mesh = mesh_data.value; - const uint64_t mesh_id = mesh_data.key; - ImporterMeshInstance3D *mesh_instance = mesh->godot_mesh_instance; - const int mesh_weights = mesh->max_weight_count; - Ref<FBXSkeleton> skeleton; - const bool valid_armature = mesh->valid_armature_id; - const uint64_t armature = mesh->armature_id; - - if (mesh_weights > 0) { - // this is a bug, it means the weights were found but the skeleton wasn't - ERR_CONTINUE_MSG(!valid_armature, "[doc] fbx armature is missing"); - } else { - continue; // safe to continue not a bug just a normal mesh - } - - if (state.skeleton_map.has(armature)) { - skeleton = state.skeleton_map[armature]; - print_verbose("[doc] armature mesh to skeleton mapping has been allocated"); - } else { - print_error("[doc] unable to find armature mapping"); - } - - ERR_CONTINUE_MSG(!mesh_instance, "[doc] invalid mesh mapping for skeleton assignment"); - ERR_CONTINUE_MSG(skeleton.is_null(), "[doc] unable to resolve the correct skeleton but we have weights!"); - - mesh_instance->set_skeleton_path(mesh_instance->get_path_to(skeleton->skeleton)); - print_verbose("[doc] allocated skeleton to mesh " + mesh_instance->get_name()); - - // do we have a mesh skin for this mesh - ERR_CONTINUE_MSG(!state.MeshSkins.has(mesh_id), "no skin found for mesh"); - - Ref<Skin> mesh_skin = state.MeshSkins[mesh_id]; - - ERR_CONTINUE_MSG(mesh_skin.is_null(), "invalid skin stored in map"); - print_verbose("[doc] allocated skin to mesh " + mesh_instance->get_name()); - mesh_instance->set_skin(mesh_skin); - } - - // build skin and skeleton information - print_verbose("[doc] Skeleton3D Bone count: " + itos(state.fbx_bone_map.size())); - const FBXDocParser::FileGlobalSettings *FBXSettings = p_document->GlobalSettingsPtr(); - - // Configure constraints - // NOTE: constraints won't be added quite yet, we don't have a real need for them *yet*. (they can be supported later on) - // const std::vector<uint64_t> fbx_constraints = p_document->GetConstraintStackIDs(); - - // get the animation FPS - float fps_setting = ImportUtils::get_fbx_fps(FBXSettings); - - // enable animation import, only if local animation is enabled - if (state.enable_animation_import && (p_flags & IMPORT_ANIMATION)) { - // document animation stack list - get by ID so we can unload any non used animation stack - const std::vector<uint64_t> animation_stack = p_document->GetAnimationStackIDs(); - - for (uint64_t anim_id : animation_stack) { - FBXDocParser::LazyObject *lazyObject = p_document->GetObject(anim_id); - const FBXDocParser::AnimationStack *stack = lazyObject->Get<FBXDocParser::AnimationStack>(); - - if (stack != nullptr) { - String animation_name = ImportUtils::FBXNodeToName(stack->Name()); - print_verbose("Valid animation stack has been found: " + animation_name); - // ReferenceTime is the same for some animations? - // LocalStop time is the start and end time - float r_start = CONVERT_FBX_TIME(stack->ReferenceStart()); - float r_stop = CONVERT_FBX_TIME(stack->ReferenceStop()); - float start_time = CONVERT_FBX_TIME(stack->LocalStart()); - float end_time = CONVERT_FBX_TIME(stack->LocalStop()); - float duration = end_time - start_time; - - print_verbose("r_start " + rtos(r_start) + ", r_stop " + rtos(r_stop)); - print_verbose("start_time" + rtos(start_time) + " end_time " + rtos(end_time)); - print_verbose("anim duration : " + rtos(duration)); - - // we can safely create the animation player - if (state.animation_player == nullptr) { - print_verbose("Creating animation player"); - state.animation_player = memnew(AnimationPlayer); - state.root->add_child(state.animation_player, true); - state.animation_player->set_owner(state.root_owner); - } - - Ref<Animation> animation; - animation.instantiate(); - animation->set_name(animation_name); - animation->set_length(duration); - - print_verbose("Animation length: " + rtos(animation->get_length()) + " seconds"); - - // i think assimp was duplicating things, this lets me know to just reference or ignore this to prevent duplicate information in tracks - // this would mean that we would be doing three times as much work per track if my theory is correct. - // this was not the case but this is a good sanity check for the animation handler from the document. - // it also lets us know if the FBX specification massively changes the animation system, in theory such a change would make this show - // an fbx specification error, so best keep it in - // the overhead is tiny. - Map<uint64_t, const FBXDocParser::AnimationCurve *> CheckForDuplication; - - const std::vector<const FBXDocParser::AnimationLayer *> &layers = stack->Layers(); - print_verbose("FBX Animation layers: " + itos(layers.size())); - for (const FBXDocParser::AnimationLayer *layer : layers) { - std::vector<const FBXDocParser::AnimationCurveNode *> node_list = layer->Nodes(); - print_verbose("Layer: " + ImportUtils::FBXNodeToName(layer->Name()) + ", " + " AnimCurveNode count " + itos(node_list.size())); - - // first thing to do here is that i need to first get the animcurvenode to a Vector3 - // we now need to put this into the track information for godot. - // to do this we need to know which track is what? - - // target id, [ track name, [time index, vector] ] - // new map needs to be [ track name, keyframe_data ] - Map<uint64_t, Map<StringName, FBXTrack>> AnimCurveNodes; - - // struct AnimTrack { - // // Animation track can be - // // visible, T, R, S - // Map<StringName, Map<uint64_t, Vector3> > animation_track; - // }; - - // Map<uint64_t, AnimTrack> AnimCurveNodes; - - // so really, what does this mean to make an animtion track. - // we need to know what object the curves are for. - // we need the target ID and the target name for the track reduction. - - FBXDocParser::Model::RotOrder quaternion_rotation_order = FBXDocParser::Model::RotOrder_EulerXYZ; - - // T:: R:: S:: Visible:: Custom:: - for (const FBXDocParser::AnimationCurveNode *curve_node : node_list) { - // when Curves() is called the curves are actually read, we could replace this with our own ProcessDomConnection code here if required. - // We may need to do this but ideally we use Curves - // note: when you call this there might be a delay in opening it - // uses mutable type to 'cache' the response until the AnimationCurveNode is cleaned up. - std::map<std::string, const FBXDocParser::AnimationCurve *> curves = curve_node->Curves(); - const FBXDocParser::Object *object = curve_node->Target(); - const FBXDocParser::Model *target = curve_node->TargetAsModel(); - if (target == nullptr) { - if (object != nullptr) { - print_error("[doc] warning failed to find a target Model for curve: " + String(object->Name().c_str())); - } else { - //print_error("[doc] failed to resolve object"); - continue; - } - - continue; - } else { - //print_verbose("[doc] applied rotation order: " + itos(target->RotationOrder())); - quaternion_rotation_order = target->RotationOrder(); - } - - uint64_t target_id = target->ID(); - String target_name = ImportUtils::FBXNodeToName(target->Name()); - - 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); - float offset_z = FBXDocParser::PropertyGet<float>(properties, "d|Z", got_z); - - String curve_node_name = ImportUtils::FBXNodeToName(curve_node->Name()); - - // Reduce all curves for this node into a single container - // T, R, S is what we expect, although other tracks are possible - // like for example visibility tracks. - - // We are not ordered here, we don't care about ordering, this happens automagically by godot when we insert with the - // key time :), so order is unimportant because the insertion will happen at a time index - // good to know: we do not need a list of these in another format :) - //Map<String, Vector<const Assimp::FBX::AnimationCurve *> > unordered_track; - - // T - // R - // S - // Map[String, List<VECTOR>] - - // So this is a reduction of the animation curve nodes - // We build this as a lookup, this is essentially our 'animation track' - //AnimCurveNodes.insert(curve_node_name, Map<uint64_t, Vector3>()); - - // create the animation curve information with the target id - // so the point of this makes a track with the name "T" for example - // the target ID is also set here, this means we don't need to do anything extra when we are in the 'create all animation tracks' step - FBXTrack &keyframe_map = AnimCurveNodes[target_id][StringName(curve_node_name)]; - - if (got_x && got_y && got_z) { - Vector3 default_value = Vector3(offset_x, offset_y, offset_z); - keyframe_map.default_value = default_value; - keyframe_map.has_default = true; - //print_verbose("track name: " + curve_node_name); - //print_verbose("xyz default: " + default_value); - } - // target id, [ track name, [time index, vector] ] - // Map<uint64_t, Map<StringName, Map<uint64_t, Vector3> > > AnimCurveNodes; - - // we probably need the target id here. - // so map[uint64_t map]... - // Map<uint64_t, Vector3D> translation_keys, rotation_keys, scale_keys; - - // extra const required by C++11 colon/Range operator - // note: do not use C++17 syntax here for dicts. - // this is banned in Godot. - for (std::pair<const std::string, const FBXDocParser::AnimationCurve *> &kvp : curves) { - const String curve_element = ImportUtils::FBXNodeToName(kvp.first); - const FBXDocParser::AnimationCurve *curve = kvp.second; - String curve_name = ImportUtils::FBXNodeToName(curve->Name()); - uint64_t curve_id = curve->ID(); - - if (CheckForDuplication.has(curve_id)) { - print_error("(FBX spec changed?) We found a duplicate curve being used for an alternative node - report to godot issue tracker"); - } else { - CheckForDuplication.insert(curve_id, curve); - } - - // FBX has no name for AnimCurveNode::, most of the time, not seen any with valid name here. - const std::map<int64_t, float> &track_time = curve->GetValueTimeTrack(); - - if (track_time.size() > 0) { - for (std::pair<int64_t, float> keyframe : track_time) { - if (curve_element == "d|X") { - keyframe_map.keyframes[keyframe.first].x = keyframe.second; - } else if (curve_element == "d|Y") { - keyframe_map.keyframes[keyframe.first].y = keyframe.second; - } else if (curve_element == "d|Z") { - keyframe_map.keyframes[keyframe.first].z = keyframe.second; - } else { - //print_error("FBX Unsupported element: " + curve_element); - } - - //print_verbose("[" + itos(target_id) + "] Keyframe added: " + itos(keyframe_map.size())); - - //print_verbose("Keyframe t:" + rtos(animation_track_time) + " v: " + rtos(keyframe.second)); - } - } - } - } - - // Map<uint64_t, Map<StringName, Map<uint64_t, Vector3> > > AnimCurveNodes; - // add this animation track here - - // target id, [ track name, [time index, vector] ] - //std::map<uint64_t, std::map<StringName, FBXTrack > > AnimCurveNodes; - for (KeyValue<uint64_t, Map<StringName, FBXTrack>> &track : AnimCurveNodes) { - // 5 tracks - // current track index - // track count is 5 - // track count is 5. - // next track id is 5. - const uint64_t target_id = track.key; - - Ref<FBXBone> bone; - - // note we must not run the below code if the entry doesn't exist, it will create dummy entries which is very bad. - // remember that state.fbx_bone_map[target_id] will create a new entry EVEN if you only read. - // this would break node animation targets, so if you change this be warned. :) - if (state.fbx_bone_map.has(target_id)) { - bone = state.fbx_bone_map[target_id]; - } - - Transform3D target_transform; - - if (state.fbx_target_map.has(target_id)) { - Ref<FBXNode> node_ref = state.fbx_target_map[target_id]; - target_transform = node_ref->pivot_transform->GlobalTransform; - //print_verbose("[doc] allocated animation node transform"); - } - - //int size_targets = state.fbx_target_map.size(); - //print_verbose("Target ID map: " + itos(size_targets)); - //print_verbose("[doc] debug bone map size: " + itos(state.fbx_bone_map.size())); - - // if this is a skeleton mapped track we can just set the path for the track. - // todo: implement node paths here at some - NodePath track_path; - if (state.fbx_bone_map.size() > 0 && state.fbx_bone_map.has(target_id)) { - if (bone->fbx_skeleton.is_valid() && bone.is_valid()) { - Ref<FBXSkeleton> fbx_skeleton = bone->fbx_skeleton; - String bone_path = state.root->get_path_to(fbx_skeleton->skeleton); - bone_path += ":" + fbx_skeleton->skeleton->get_bone_name(bone->godot_bone_id); - print_verbose("[doc] track bone path: " + bone_path); - track_path = bone_path; - } - } else if (state.fbx_target_map.has(target_id)) { - //print_verbose("[doc] we have a valid target for a node animation"); - Ref<FBXNode> target_node = state.fbx_target_map[target_id]; - if (target_node.is_valid() && target_node->godot_node != nullptr) { - String node_path = state.root->get_path_to(target_node->godot_node); - track_path = node_path; - //print_verbose("[doc] node animation path: " + node_path); - } - } else { - // note: this could actually be unsafe this means we should be careful about continuing here, if we see bizarre effects later we should disable this. - // I am not sure if this is unsafe or not, testing will tell us this. - print_error("[doc] invalid fbx target detected for this track"); - continue; - } - - // everything in FBX and Maya is a node therefore if this happens something is seriously broken. - if (!state.fbx_target_map.has(target_id)) { - print_error("unable to resolve this to an FBX object."); - continue; - } - - Ref<FBXNode> target_node = state.fbx_target_map[target_id]; - const FBXDocParser::Model *model = target_node->fbx_model; - const FBXDocParser::PropertyTable *props = dynamic_cast<const FBXDocParser::PropertyTable *>(model); - - Map<StringName, FBXTrack> &track_data = track.value; - FBXTrack &translation_keys = track_data[StringName("T")]; - FBXTrack &rotation_keys = track_data[StringName("R")]; - FBXTrack &scale_keys = track_data[StringName("S")]; - - double increment = 1.0f / fps_setting; - double time = 0.0f; - - bool last = false; - - Vector<Vector3> pos_values; - Vector<float> pos_times; - Vector<Vector3> scale_values; - Vector<float> scale_times; - Vector<Quaternion> rot_values; - Vector<float> rot_times; - - double max_duration = 0; - double anim_length = animation->get_length(); - - for (std::pair<int64_t, Vector3> position_key : translation_keys.keyframes) { - pos_values.push_back(position_key.second * state.scale); - double animation_track_time = CONVERT_FBX_TIME(position_key.first); - - if (animation_track_time > max_duration) { - max_duration = animation_track_time; - } - - //print_verbose("pos keyframe: t:" + rtos(animation_track_time) + " value " + position_key.second); - pos_times.push_back(animation_track_time); - } - - for (std::pair<int64_t, Vector3> scale_key : scale_keys.keyframes) { - scale_values.push_back(scale_key.second); - double animation_track_time = CONVERT_FBX_TIME(scale_key.first); - - if (animation_track_time > max_duration) { - max_duration = animation_track_time; - } - //print_verbose("scale keyframe t:" + rtos(animation_track_time)); - scale_times.push_back(animation_track_time); - } - - // - // Pre and Post keyframe rotation handler - // -- Required because Maya and Autodesk <3 the pain when it comes to implementing animation code! enjoy <3 - - bool got_pre = false; - bool got_post = false; - - Quaternion post_rotation; - Quaternion pre_rotation; - - // Rotation matrix - const Vector3 &PreRotation = FBXDocParser::PropertyGet<Vector3>(props, "PreRotation", got_pre); - const Vector3 &PostRotation = FBXDocParser::PropertyGet<Vector3>(props, "PostRotation", got_post); - - FBXDocParser::Model::RotOrder rot_order = model->RotationOrder(); - if (got_pre) { - pre_rotation = ImportUtils::EulerToQuaternion(rot_order, ImportUtils::deg2rad(PreRotation)); - } - if (got_post) { - post_rotation = ImportUtils::EulerToQuaternion(rot_order, ImportUtils::deg2rad(PostRotation)); - } - - Quaternion lastQuaternion = Quaternion(); - - for (std::pair<int64_t, Vector3> rotation_key : rotation_keys.keyframes) { - double animation_track_time = CONVERT_FBX_TIME(rotation_key.first); - - //print_verbose("euler rotation key: " + rotation_key.second); - Quaternion rot_key_value = ImportUtils::EulerToQuaternion(quaternion_rotation_order, ImportUtils::deg2rad(rotation_key.second)); - - if (lastQuaternion != Quaternion() && rot_key_value.dot(lastQuaternion) < 0) { - rot_key_value.x = -rot_key_value.x; - rot_key_value.y = -rot_key_value.y; - rot_key_value.z = -rot_key_value.z; - rot_key_value.w = -rot_key_value.w; - } - // pre_post rotation possibly could fix orientation - Quaternion final_rotation = pre_rotation * rot_key_value * post_rotation; - - lastQuaternion = final_rotation; - - if (animation_track_time > max_duration) { - max_duration = animation_track_time; - } - - rot_values.push_back(final_rotation.normalized()); - rot_times.push_back(animation_track_time); - } - - bool valid_rest = false; - Transform3D bone_rest; - int skeleton_bone = -1; - if (state.fbx_bone_map.has(target_id)) { - if (bone.is_valid() && bone->fbx_skeleton.is_valid()) { - skeleton_bone = bone->godot_bone_id; - if (skeleton_bone >= 0) { - bone_rest = bone->fbx_skeleton->skeleton->get_bone_rest(skeleton_bone); - valid_rest = true; - } - } - - if (!valid_rest) { - print_verbose("invalid rest!"); - } - } - - const Vector3 def_pos = translation_keys.has_default ? (translation_keys.default_value * state.scale) : bone_rest.origin; - const Quaternion def_rot = rotation_keys.has_default ? ImportUtils::EulerToQuaternion(quaternion_rotation_order, ImportUtils::deg2rad(rotation_keys.default_value)) : bone_rest.basis.get_rotation_quaternion(); - const Vector3 def_scale = scale_keys.has_default ? scale_keys.default_value : bone_rest.basis.get_scale(); - print_verbose("track defaults: p(" + def_pos + ") s(" + def_scale + ") r(" + def_rot + ")"); - - int position_idx = -1; - if (pos_values.size()) { - position_idx = animation->get_track_count(); - animation->add_track(Animation::TYPE_POSITION_3D); - animation->track_set_path(position_idx, track_path); - animation->track_set_imported(position_idx, true); - } - - int rotation_idx = -1; - if (pos_values.size()) { - rotation_idx = animation->get_track_count(); - animation->add_track(Animation::TYPE_ROTATION_3D); - animation->track_set_path(rotation_idx, track_path); - animation->track_set_imported(rotation_idx, true); - } - - int scale_idx = -1; - if (pos_values.size()) { - scale_idx = animation->get_track_count(); - animation->add_track(Animation::TYPE_SCALE_3D); - animation->track_set_path(scale_idx, track_path); - animation->track_set_imported(scale_idx, true); - } - - while (true) { - Vector3 pos = def_pos; - Quaternion rot = def_rot; - Vector3 scale = def_scale; - - if (pos_values.size()) { - pos = _interpolate_track<Vector3>(pos_times, pos_values, time, - AssetImportAnimation::INTERP_LINEAR); - } - - if (rot_values.size()) { - rot = _interpolate_track<Quaternion>(rot_times, rot_values, time, - AssetImportAnimation::INTERP_LINEAR); - } - - if (scale_values.size()) { - scale = _interpolate_track<Vector3>(scale_times, scale_values, time, - AssetImportAnimation::INTERP_LINEAR); - } - - if (position_idx >= 0) { - animation->position_track_insert_key(position_idx, time, pos); - } - if (rotation_idx >= 0) { - animation->rotation_track_insert_key(rotation_idx, time, rot); - } - if (scale_idx >= 0) { - animation->scale_track_insert_key(scale_idx, time, scale); - } - - if (last) { - break; - } - - time += increment; - if (time > anim_length) { - last = true; - time = anim_length; - break; - } - } - } - } - state.animation_player->add_animation(animation_name, animation); - } - } - - // AnimStack elements contain start stop time and name of animation - // AnimLayer is the current active layer of the animation (multiple layers can be active we only support 1) - // AnimCurveNode has a OP link back to the model which is the real node. - // AnimCurveNode has a direct link to AnimationCurve (of which it may have more than one) - - // Store animation stack in list - // iterate over all AnimStacks like the cache node algorithm recursively - // this can then be used with ProcessDomConnection<> to link from - // AnimStack:: <-- (OO) --> AnimLayer:: <-- (OO) --> AnimCurveNode:: (which can OP resolve) to Model:: - } - - // - // Cleanup operations - explicit to prevent errors on shutdown - found that ref to ref does behave badly sometimes. - // - - state.renderer_mesh_data.clear(); - state.MeshSkins.clear(); - state.fbx_target_map.clear(); - state.fbx_node_list.clear(); - - for (KeyValue<uint64_t, Ref<FBXBone>> &element : state.fbx_bone_map) { - Ref<FBXBone> bone = element.value; - bone->parent_bone.unref(); - bone->node.unref(); - bone->fbx_skeleton.unref(); - } - - for (KeyValue<uint64_t, Ref<FBXSkeleton>> &element : state.skeleton_map) { - Ref<FBXSkeleton> skel = element.value; - skel->fbx_node.unref(); - skel->skeleton_bones.clear(); - } - - state.fbx_bone_map.clear(); - state.skeleton_map.clear(); - state.fbx_root_node.unref(); - - return scene_root; -} - -void EditorSceneFormatImporterFBX::BuildDocumentBones(Ref<FBXBone> p_parent_bone, - ImportState &state, const FBXDocParser::Document *p_doc, - uint64_t p_id) { - const std::vector<const FBXDocParser::Connection *> &conns = p_doc->GetConnectionsByDestinationSequenced(p_id, "Model"); - // FBX can do an join like this - // Model -> SubDeformer (bone) -> Deformer (skin pose) - // This is important because we need to somehow link skin back to bone id in skeleton :) - // The rules are: - // A subdeformer will exist if 'limbnode' class tag present - // The subdeformer will not necessarily have a deformer as joints do not have one - for (const FBXDocParser::Connection *con : conns) { - // goto: bone creation - //print_verbose("con: " + String(con->PropertyName().c_str())); - - // ignore object-property links we want the object to object links nothing else - if (con->PropertyName().length()) { - continue; - } - - // convert connection source object into Object base class - const FBXDocParser::Object *const object = con->SourceObject(); - - if (nullptr == object) { - print_verbose("failed to convert source object for Model link"); - continue; - } - - // FBX Model::Cube, Model::Bone001, etc elements - // This detects if we can cast the object into this model structure. - const FBXDocParser::Model *const model = dynamic_cast<const FBXDocParser::Model *>(object); - - // declare our bone element reference (invalid, unless we create a bone in this step) - // this lets us pass valid armature information into children objects and this is why we moved this up here - // previously this was created .instantiated() on the same line. - Ref<FBXBone> bone_element; - - if (model != nullptr) { - // model marked with limb node / casted. - const FBXDocParser::ModelLimbNode *const limb_node = dynamic_cast<const FBXDocParser::ModelLimbNode *>(model); - if (limb_node != nullptr) { - // Write bone into bone list for FBX - - ERR_FAIL_COND_MSG(state.fbx_bone_map.has(limb_node->ID()), "[serious] duplicate LimbNode detected"); - - bool parent_is_bone = state.fbx_bone_map.find(p_id); - bone_element.instantiate(); - - // used to build the bone hierarchy in the skeleton - bone_element->parent_bone_id = parent_is_bone ? p_id : 0; - bone_element->valid_parent = parent_is_bone; - bone_element->limb_node = limb_node; - - // parent is a node and this is the first bone - if (!parent_is_bone) { - uint64_t armature_id = p_id; - bone_element->valid_armature_id = true; - bone_element->armature_id = armature_id; - print_verbose("[doc] valid armature has been configured for first child: " + itos(armature_id)); - } else if (p_parent_bone.is_valid()) { - if (p_parent_bone->valid_armature_id) { - bone_element->valid_armature_id = true; - bone_element->armature_id = p_parent_bone->armature_id; - print_verbose("[doc] bone has valid armature id:" + itos(bone_element->armature_id)); - } else { - print_error("[doc] unassigned armature id: " + String(limb_node->Name().c_str())); - } - } else { - print_error("[doc] error is this a bone? " + String(limb_node->Name().c_str())); - } - - if (!parent_is_bone) { - print_verbose("[doc] Root bone: " + bone_element->bone_name); - } - - uint64_t limb_id = limb_node->ID(); - bone_element->bone_id = limb_id; - bone_element->bone_name = ImportUtils::FBXNodeToName(model->Name()); - bone_element->parent_bone = p_parent_bone; - - // insert limb by ID into list. - state.fbx_bone_map.insert(limb_node->ID(), bone_element); - } - - // recursion call - child nodes - BuildDocumentBones(bone_element, state, p_doc, model->ID()); - } - } -} - -void EditorSceneFormatImporterFBX::BuildDocumentNodes( - Ref<PivotTransform> parent_transform, - ImportState &state, - const FBXDocParser::Document *p_doc, - uint64_t id, - Ref<FBXNode> parent_node) { - // tree - // here we get the node 0 on the root by default - const std::vector<const FBXDocParser::Connection *> &conns = p_doc->GetConnectionsByDestinationSequenced(id, "Model"); - - // branch - for (const FBXDocParser::Connection *con : conns) { - // ignore object-property links - if (con->PropertyName().length()) { - // really important we document why this is ignored. - print_verbose("ignoring property link - no docs on why this is ignored"); - continue; - } - - // convert connection source object into Object base class - // Source objects can exist with 'null connections' this means that we only for sure know the source exists. - const FBXDocParser::Object *const source_object = con->SourceObject(); - - if (nullptr == source_object) { - print_verbose("failed to convert source object for Model link"); - continue; - } - - // FBX Model::Cube, Model::Bone001, etc elements - // This detects if we can cast the object into this model structure. - const FBXDocParser::Model *const model = dynamic_cast<const FBXDocParser::Model *>(source_object); - // model is the current node - if (nullptr != model) { - uint64_t current_node_id = model->ID(); - - Ref<FBXNode> new_node; - new_node.instantiate(); - new_node->current_node_id = current_node_id; - new_node->node_name = ImportUtils::FBXNodeToName(model->Name()); - - Ref<PivotTransform> fbx_transform; - fbx_transform.instantiate(); - fbx_transform->set_parent(parent_transform); - fbx_transform->set_model(model); - fbx_transform->debug_pivot_xform("name: " + new_node->node_name); - fbx_transform->Execute(); - - new_node->set_pivot_transform(fbx_transform); - - // check if this node is a bone - if (state.fbx_bone_map.has(current_node_id)) { - Ref<FBXBone> bone = state.fbx_bone_map[current_node_id]; - if (bone.is_valid()) { - bone->set_node(new_node); - print_verbose("allocated bone data: " + bone->bone_name); - } - } - - // set the model, we can't just assign this safely - new_node->set_model(model); - - if (parent_node.is_valid()) { - new_node->set_parent(parent_node); - } else { - new_node->set_parent(state.fbx_root_node); - } - - CRASH_COND_MSG(new_node->pivot_transform.is_null(), "invalid fbx target map pivot transform [serious]"); - - // populate lookup tables with references - // [fbx_node_id, fbx_node] - - state.fbx_node_list.push_back(new_node); - if (!state.fbx_target_map.has(new_node->current_node_id)) { - state.fbx_target_map[new_node->current_node_id] = new_node; - } - - // print node name - print_verbose("[doc] new node " + new_node->node_name); - - // sub branches - BuildDocumentNodes(new_node->pivot_transform, state, p_doc, current_node_id, new_node); - } - } -} diff --git a/modules/fbx/editor_scene_importer_fbx.h b/modules/fbx/editor_scene_importer_fbx.h deleted file mode 100644 index 7845e079c2..0000000000 --- a/modules/fbx/editor_scene_importer_fbx.h +++ /dev/null @@ -1,134 +0,0 @@ -/*************************************************************************/ -/* editor_scene_importer_fbx.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef EDITOR_SCENE_IMPORTER_FBX_H -#define EDITOR_SCENE_IMPORTER_FBX_H - -#ifdef TOOLS_ENABLED - -#include "data/import_state.h" -#include "tools/import_utils.h" - -#include "core/io/resource_importer.h" -#include "core/string/ustring.h" -#include "core/templates/local_vector.h" -#include "core/templates/vector.h" -#include "core/variant/dictionary.h" -#include "editor/import/resource_importer_scene.h" -#include "editor/project_settings_editor.h" -#include "scene/3d/mesh_instance_3d.h" -#include "scene/3d/node_3d.h" -#include "scene/3d/skeleton_3d.h" -#include "scene/animation/animation_player.h" -#include "scene/resources/animation.h" -#include "scene/resources/surface_tool.h" - -#include "fbx_parser/FBXDocument.h" -#include "fbx_parser/FBXImportSettings.h" -#include "fbx_parser/FBXMeshGeometry.h" -#include "fbx_parser/FBXUtil.h" - -#define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000LL - -class EditorSceneFormatImporterFBX : public EditorSceneFormatImporter { -private: - GDCLASS(EditorSceneFormatImporterFBX, EditorSceneFormatImporter); - - struct AssetImportAnimation { - enum Interpolation { - INTERP_LINEAR, - INTERP_STEP, - INTERP_CATMULLROMSPLINE, - INTERP_CUBIC_SPLINE - }; - }; - - // ------------------------------------------------------------------------------------------------ - template <typename T> - const T *ProcessDOMConnection( - const FBXDocParser::Document *doc, - uint64_t current_element, - bool reverse_lookup = false) { - const std::vector<const FBXDocParser::Connection *> &conns = reverse_lookup ? doc->GetConnectionsByDestinationSequenced(current_element) : doc->GetConnectionsBySourceSequenced(current_element); - //print_verbose("[doc] looking for " + String(element_to_find)); - // using the temp pattern here so we can debug before it returns - // in some cases we return too early, with 'deformer object base class' in wrong place - // in assimp this means we can accidentally return too early... - const T *return_obj = nullptr; - - for (const FBXDocParser::Connection *con : conns) { - const FBXDocParser::Object *source_object = con->SourceObject(); - const FBXDocParser::Object *dest_object = con->DestinationObject(); - if (source_object && dest_object != nullptr) { - //print_verbose("[doc] connection name: " + String(source_object->Name().c_str()) + ", dest: " + String(dest_object->Name().c_str())); - const T *temp = dynamic_cast<const T *>(reverse_lookup ? source_object : dest_object); - if (temp) { - return_obj = temp; - } - } - } - - if (return_obj != nullptr) { - //print_verbose("[doc] returned valid element"); - //print_verbose("Found object for bone"); - return return_obj; - } - - // safe to return nothing, need to use nullptr here as nullptr is used internally for FBX document. - return nullptr; - } - - void BuildDocumentBones(Ref<FBXBone> p_parent_bone, - ImportState &state, const FBXDocParser::Document *p_doc, - uint64_t p_id); - - void BuildDocumentNodes(Ref<PivotTransform> parent_transform, ImportState &state, const FBXDocParser::Document *doc, uint64_t id, Ref<FBXNode> fbx_parent); - - Node3D *_generate_scene(const String &p_path, const FBXDocParser::Document *p_document, - const uint32_t p_flags, - int p_bake_fps, - const int32_t p_max_bone_weights, - bool p_is_blender_fbx); - - template <class T> - T _interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time, AssetImportAnimation::Interpolation p_interp); - void _register_project_setting_import(const String generic, const String import_setting_string, const Vector<String> &exts, List<String> *r_extensions, const bool p_enabled) const; - -public: - EditorSceneFormatImporterFBX() {} - ~EditorSceneFormatImporterFBX() {} - - virtual void get_extensions(List<String> *r_extensions) const override; - virtual uint32_t get_import_flags() const override; - virtual Node3D *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = nullptr) override; -}; - -#endif // TOOLS_ENABLED -#endif // EDITOR_SCENE_IMPORTER_FBX_H diff --git a/modules/fbx/fbx_parser/ByteSwapper.h b/modules/fbx/fbx_parser/ByteSwapper.h deleted file mode 100644 index 08d38147d5..0000000000 --- a/modules/fbx/fbx_parser/ByteSwapper.h +++ /dev/null @@ -1,283 +0,0 @@ -/*************************************************************************/ -/* ByteSwapper.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2020, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file Helper class tp perform various byte order swappings - (e.g. little to big endian) */ -#ifndef BYTE_SWAPPER_H -#define BYTE_SWAPPER_H - -#include <stdint.h> -#include <algorithm> -#include <locale> - -namespace FBXDocParser { -// -------------------------------------------------------------------------------------- -/** Defines some useful byte order swap routines. - * - * This is required to read big-endian model formats on little-endian machines, - * and vice versa. Direct use of this class is DEPRECATED. Use #StreamReader instead. */ -// -------------------------------------------------------------------------------------- -class ByteSwap { - ByteSwap() {} - -public: - // ---------------------------------------------------------------------- - /** Swap two bytes of data - * @param[inout] _szOut A void* to save the reintcasts for the caller. */ - static inline void Swap2(void *_szOut) { - uint8_t *const szOut = reinterpret_cast<uint8_t *>(_szOut); - std::swap(szOut[0], szOut[1]); - } - - // ---------------------------------------------------------------------- - /** Swap four bytes of data - * @param[inout] _szOut A void* to save the reintcasts for the caller. */ - static inline void Swap4(void *_szOut) { - uint8_t *const szOut = reinterpret_cast<uint8_t *>(_szOut); - std::swap(szOut[0], szOut[3]); - std::swap(szOut[1], szOut[2]); - } - - // ---------------------------------------------------------------------- - /** Swap eight bytes of data - * @param[inout] _szOut A void* to save the reintcasts for the caller. */ - static inline void Swap8(void *_szOut) { - uint8_t *const szOut = reinterpret_cast<uint8_t *>(_szOut); - std::swap(szOut[0], szOut[7]); - std::swap(szOut[1], szOut[6]); - std::swap(szOut[2], szOut[5]); - std::swap(szOut[3], szOut[4]); - } - - // ---------------------------------------------------------------------- - /** ByteSwap a float. Not a joke. - * @param[inout] fOut ehm. .. */ - static inline void Swap(float *fOut) { - Swap4(fOut); - } - - // ---------------------------------------------------------------------- - /** ByteSwap a double. Not a joke. - * @param[inout] fOut ehm. .. */ - static inline void Swap(double *fOut) { - Swap8(fOut); - } - - // ---------------------------------------------------------------------- - /** ByteSwap an int16t. Not a joke. - * @param[inout] fOut ehm. .. */ - static inline void Swap(int16_t *fOut) { - Swap2(fOut); - } - - static inline void Swap(uint16_t *fOut) { - Swap2(fOut); - } - - // ---------------------------------------------------------------------- - /** ByteSwap an int32t. Not a joke. - * @param[inout] fOut ehm. .. */ - static inline void Swap(int32_t *fOut) { - Swap4(fOut); - } - - static inline void Swap(uint32_t *fOut) { - Swap4(fOut); - } - - // ---------------------------------------------------------------------- - /** ByteSwap an int64t. Not a joke. - * @param[inout] fOut ehm. .. */ - static inline void Swap(int64_t *fOut) { - Swap8(fOut); - } - - static inline void Swap(uint64_t *fOut) { - Swap8(fOut); - } - - // ---------------------------------------------------------------------- - //! Templatized ByteSwap - //! \returns param tOut as swapped - template <typename Type> - static inline Type Swapped(Type tOut) { - return _swapper<Type, sizeof(Type)>()(tOut); - } - -private: - template <typename T, size_t size> - struct _swapper; -}; - -template <typename T> -struct ByteSwap::_swapper<T, 2> { - T operator()(T tOut) { - Swap2(&tOut); - return tOut; - } -}; - -template <typename T> -struct ByteSwap::_swapper<T, 4> { - T operator()(T tOut) { - Swap4(&tOut); - return tOut; - } -}; - -template <typename T> -struct ByteSwap::_swapper<T, 8> { - T operator()(T tOut) { - Swap8(&tOut); - return tOut; - } -}; - -// -------------------------------------------------------------------------------------- -// ByteSwap macros for BigEndian/LittleEndian support -// -------------------------------------------------------------------------------------- -#if (defined AI_BUILD_BIG_ENDIAN) -#define AI_LE(t) (t) -#define AI_BE(t) ByteSwap::Swapped(t) -#define AI_LSWAP2(p) -#define AI_LSWAP4(p) -#define AI_LSWAP8(p) -#define AI_LSWAP2P(p) -#define AI_LSWAP4P(p) -#define AI_LSWAP8P(p) -#define LE_NCONST const -#define AI_SWAP2(p) ByteSwap::Swap2(&(p)) -#define AI_SWAP4(p) ByteSwap::Swap4(&(p)) -#define AI_SWAP8(p) ByteSwap::Swap8(&(p)) -#define AI_SWAP2P(p) ByteSwap::Swap2((p)) -#define AI_SWAP4P(p) ByteSwap::Swap4((p)) -#define AI_SWAP8P(p) ByteSwap::Swap8((p)) -#define BE_NCONST -#else -#define AI_BE(t) (t) -#define AI_LE(t) ByteSwap::Swapped(t) -#define AI_SWAP2(p) -#define AI_SWAP4(p) -#define AI_SWAP8(p) -#define AI_SWAP2P(p) -#define AI_SWAP4P(p) -#define AI_SWAP8P(p) -#define BE_NCONST const -#define AI_LSWAP2(p) ByteSwap::Swap2(&(p)) -#define AI_LSWAP4(p) ByteSwap::Swap4(&(p)) -#define AI_LSWAP8(p) ByteSwap::Swap8(&(p)) -#define AI_LSWAP2P(p) ByteSwap::Swap2((p)) -#define AI_LSWAP4P(p) ByteSwap::Swap4((p)) -#define AI_LSWAP8P(p) ByteSwap::Swap8((p)) -#define LE_NCONST -#endif - -namespace Intern { - -// -------------------------------------------------------------------------------------------- -template <typename T, bool doit> -struct ByteSwapper { - void operator()(T *inout) { - ByteSwap::Swap(inout); - } -}; - -template <typename T> -struct ByteSwapper<T, false> { - void operator()(T *) { - } -}; - -// -------------------------------------------------------------------------------------------- -template <bool SwapEndianess, typename T, bool RuntimeSwitch> -struct Getter { - void operator()(T *inout, bool le) { - le = !le; - if (le) { - ByteSwapper<T, (sizeof(T) > 1 ? true : false)>()(inout); - } else { - ByteSwapper<T, false>()(inout); - } - } -}; - -template <bool SwapEndianess, typename T> -struct Getter<SwapEndianess, T, false> { - void operator()(T *inout, bool /*le*/) { - // static branch - ByteSwapper<T, (SwapEndianess && sizeof(T) > 1)>()(inout); - } -}; -} // namespace Intern -} // namespace FBXDocParser - -#endif // BYTE_SWAPPER_H diff --git a/modules/fbx/fbx_parser/CREDITS b/modules/fbx/fbx_parser/CREDITS deleted file mode 100644 index 62b449614e..0000000000 --- a/modules/fbx/fbx_parser/CREDITS +++ /dev/null @@ -1,183 +0,0 @@ -=============================================================== -Open Asset Import Library (Assimp) -Developers and Contributors -=============================================================== - -The following is a non-exhaustive list of all constributors over the years. -If you think your name should be listed here, drop us a line and we'll add you. - -- Alexander Gessler, -3DS-, BLEND-, ASE-, DXF-, HMP-, MDL-, MD2-, MD3-, MD5-, MDC-, NFF-, PLY-, STL-, RAW-, OFF-, MS3D-, Q3D- and LWO-Loader, Assimp-Viewer, assimp-cmd, -noboost, Website (Design). - -- Thomas Schulze, -X-, Collada-, BVH-Loader, Postprocessing framework. Data structure & Interface design, documentation. - -- Kim Kulling, -Obj-, Q3BSD-, OpenGEX-Loader, Logging system, CMake-build-environment, Linux-build, Website ( Admin ), Coverity ( Admin ), Glitter ( Admin ). - -- R.Schmidt, -Linux build, eclipse support. - -- Matthias Gubisch, -Assimp.net -Visual Studio 9 support, bugfixes. - -- Mark Sibly -B3D-Loader, Assimp testing - -- Jonathan Klein -Ogre Loader, VC2010 fixes and CMake fixes. - -- Sebastian Hempel, -PyAssimp (first version) -Compile-Bugfixes for mingw, add environment for static library support in make. - -- Jonathan Pokrass -Supplied a bugfix concerning the scaling in the md3 loader. - -- Andrew Galante, -Submitted patches to make Assimp compile with GCC-4, a makefile and the xcode3 workspace. - -- Andreas Nagel -First Assimp testing & verification under Windows Vista 64 Bit. - -- Marius Schr�der -Allowed us to use many of his models for screenshots and testing. - -- Christian Schubert -Supplied various XFiles for testing purposes. - -- Tizian Wieland -Searched the web for hundreds of test models for internal use - -- John Connors -Supplied patches for linux and SCons. - -- T. R. -The GUY who performed some of the CSM mocaps. - -- Andy Maloney -Contributed fixes for the documentation and the doxygen markup - -- Zhao Lei -Contributed several bugfixes fixing memory leaks and improving float parsing - -- sueastside -Updated PyAssimp to the latest Assimp data structures and provided a script to keep the Python binding up-to-date. - -- Tobias Rittig -Collada testing with Cinema 4D - -- Brad Grantham -Improvements in OpenGL-Sample. - -- Robert Ramirez -Add group loading feature to Obj-Loader. - -- Chris Maiwald -Many bugreports, improving Assimp's portability, regular testing & feedback. - -- Stepan Hrbek -Bugreport and fix for a obj-materialloader crash. - -- David Nadlinger -D bindings, CMake install support. - -- Dario Accornero -Contributed several patches regarding Mac OS/XCode targets, bug reports. - -- Martin Walser (Samhayne) -Contributed the 'SimpleTexturedOpenGl' sample. - -- Matthias Fauconneau -Contributed a fix for the Q3-BSP loader. - -- Jørgen P. Tjernø -Contributed updated and improved xcode workspaces - -- drparallax -Contributed the /samples/SimpleAssimpViewX sample - -- Carsten Fuchs -Contributed a fix for the Normalize method in aiQuaternion. - -- dbburgess -Contributes a Android-specific build issue: log the hardware architecture for ARM. - -- alfiereinre7 -Contributes a obj-fileparser fix: missing tokens in the obj-token list. - -- Roman Kharitonov -Contributes a fix for the configure script environment. - -- Ed Diana -Contributed AssimpDelphi (/port/AssimpDelphi). - -- rdb -Contributes a bundle of fixes and improvements for the bsp-importer. - -- Mick P -For contributing the De-bone postprocessing step and filing various bug reports. - -- Rosen Diankov -Contributed patches to build assimp debian packages using cmake. - -- Mark Page -Contributed a patch to fix the VertexTriangleAdjacency postprocessing step. - -- IOhannes -Contributed the Debian build fixes ( architecture macro ). - -- gellule -Several LWO and LWS fixes (pivoting). - -- Marcel Metz -GCC/Linux fixes for the SimpleOpenGL sample. - -- Brian Miller -Bugfix for a compiler fix for iOS on arm. - -- Séverin Lemaignan -Rewrite of PyAssimp, distutils and Python3 support - -- albert-wang -Bugfixes for the collada parser - -- Ya ping Jin -Bugfixes for uv-tanget calculation. - -- Jonne Nauha -Ogre Binary format support - -- Filip Wasil, Tieto Poland Sp. z o.o. -Android JNI asset extraction support - -- Richard Steffen -Contributed ExportProperties interface -Contributed X File exporter -Contributed Step (stp) exporter - -- Thomas Iorns (mesilliac) -Initial FBX Export support - -For a more detailed list just check: https://github.com/assimp/assimp/network/members - - -======== -Patreons -======== - -Huge thanks to our Patreons! - -- migenius -- Marcus -- Cort -- elect -- Steffen - - -=================== -Commercial Sponsors -=================== - -- MyDidimo (mydidimo.com): Sponsored development of FBX Export support diff --git a/modules/fbx/fbx_parser/FBXAnimation.cpp b/modules/fbx/fbx_parser/FBXAnimation.cpp deleted file mode 100644 index 0fbff035fd..0000000000 --- a/modules/fbx/fbx_parser/FBXAnimation.cpp +++ /dev/null @@ -1,273 +0,0 @@ -/*************************************************************************/ -/* FBXAnimation.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXAnimation.cpp - * @brief Assimp::FBX::AnimationCurve, Assimp::FBX::AnimationCurveNode, - * Assimp::FBX::AnimationLayer, Assimp::FBX::AnimationStack - */ - -#include "FBXCommon.h" -#include "FBXDocument.h" -#include "FBXDocumentUtil.h" -#include "FBXParser.h" - -namespace FBXDocParser { - -using namespace Util; - -// ------------------------------------------------------------------------------------------------ -AnimationCurve::AnimationCurve(uint64_t id, const ElementPtr element, const std::string &name, const Document & /*doc*/) : - Object(id, element, name) { - const ScopePtr sc = GetRequiredScope(element); - const ElementPtr KeyTime = GetRequiredElement(sc, "KeyTime"); - const ElementPtr KeyValueFloat = GetRequiredElement(sc, "KeyValueFloat"); - - // note preserved keys and values for legacy FBXConverter.cpp - // we can remove this once the animation system is written - // and clean up this code so we do not have copies everywhere. - ParseVectorDataArray(keys, KeyTime); - ParseVectorDataArray(values, KeyValueFloat); - - if (keys.size() != values.size()) { - DOMError("the number of key times does not match the number of keyframe values", KeyTime); - } - - // put the two lists into the map, underlying container is really just a dictionary - // these will always match, if not an error will throw and the file will not import - // this is useful because we then can report something and fix this later if it becomes an issue - // at this point we do not need a different count of these elements so this makes the - // most sense to do. - for (size_t x = 0; x < keys.size(); x++) { - keyvalues[keys[x]] = values[x]; - } - - const ElementPtr KeyAttrDataFloat = sc->GetElement("KeyAttrDataFloat"); - if (KeyAttrDataFloat) { - ParseVectorDataArray(attributes, KeyAttrDataFloat); - } - - const ElementPtr KeyAttrFlags = sc->GetElement("KeyAttrFlags"); - if (KeyAttrFlags) { - ParseVectorDataArray(flags, KeyAttrFlags); - } -} - -// ------------------------------------------------------------------------------------------------ -AnimationCurve::~AnimationCurve() { - // empty -} - -// ------------------------------------------------------------------------------------------------ -AnimationCurveNode::AnimationCurveNode(uint64_t id, const ElementPtr element, const std::string &name, - const Document &doc, const char *const *target_prop_whitelist /*= nullptr*/, - size_t whitelist_size /*= 0*/) : - 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); - - for (const Connection *con : conns) { - // link should go for a property - if (!con->PropertyName().length()) { - continue; - } - - Object *object = con->DestinationObject(); - - if (!object) { - DOMWarning("failed to read destination object for AnimationCurveNode->Model link, ignoring", element); - continue; - } - - target = object; - prop = con->PropertyName(); - break; - } -} - -// ------------------------------------------------------------------------------------------------ -AnimationCurveNode::~AnimationCurveNode() { - curves.clear(); -} - -// ------------------------------------------------------------------------------------------------ -const AnimationMap &AnimationCurveNode::Curves() const { - /* Lazy loaded animation curves, will only load if required */ - if (curves.empty()) { - // resolve attached animation curves - const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "AnimationCurve"); - - for (const Connection *con : conns) { - // So the advantage of having this STL boilerplate is that it's dead simple once you get it. - // The other advantage is casting is guaranteed to be safe and nullptr will be returned in the last step if it fails. - Object *ob = con->SourceObject(); - AnimationCurve *anim_curve = dynamic_cast<AnimationCurve *>(ob); - ERR_CONTINUE_MSG(!anim_curve, "Failed to convert animation curve from object"); - - curves.insert(std::make_pair(con->PropertyName(), anim_curve)); - } - } - - return curves; -} - -// ------------------------------------------------------------------------------------------------ -AnimationLayer::AnimationLayer(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) : - Object(id, element, name), doc(doc) { -} - -// ------------------------------------------------------------------------------------------------ -AnimationLayer::~AnimationLayer() { - // empty -} - -// ------------------------------------------------------------------------------------------------ -const AnimationCurveNodeList AnimationLayer::Nodes(const char *const *target_prop_whitelist, - size_t whitelist_size /*= 0*/) const { - AnimationCurveNodeList nodes; - - // resolve attached animation nodes - const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "AnimationCurveNode"); - nodes.reserve(conns.size()); - - for (const Connection *con : conns) { - // link should not go to a property - if (con->PropertyName().length()) { - continue; - } - - Object *ob = con->SourceObject(); - - if (!ob) { - DOMWarning("failed to read source object for AnimationCurveNode->AnimationLayer link, ignoring", element); - continue; - } - - const AnimationCurveNode *anim = dynamic_cast<AnimationCurveNode *>(ob); - if (!anim) { - DOMWarning("source object for ->AnimationLayer link is not an AnimationCurveNode", element); - continue; - } - - if (target_prop_whitelist) { - const char *s = anim->TargetProperty().c_str(); - bool ok = false; - for (size_t i = 0; i < whitelist_size; ++i) { - if (!strcmp(s, target_prop_whitelist[i])) { - ok = true; - break; - } - } - if (!ok) { - continue; - } - } - nodes.push_back(anim); - } - - return nodes; -} - -// ------------------------------------------------------------------------------------------------ -AnimationStack::AnimationStack(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) : - Object(id, element, name) { - // resolve attached animation layers - const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "AnimationLayer"); - layers.reserve(conns.size()); - - for (const Connection *con : conns) { - // link should not go to a property - if (con->PropertyName().length()) { - continue; - } - - Object *ob = con->SourceObject(); - if (!ob) { - DOMWarning("failed to read source object for AnimationLayer->AnimationStack link, ignoring", element); - continue; - } - - const AnimationLayer *anim = dynamic_cast<const AnimationLayer *>(ob); - - if (!anim) { - DOMWarning("source object for ->AnimationStack link is not an AnimationLayer", element); - continue; - } - - layers.push_back(anim); - } -} - -// ------------------------------------------------------------------------------------------------ -AnimationStack::~AnimationStack() { -} -} // namespace FBXDocParser diff --git a/modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp b/modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp deleted file mode 100644 index d6abcbb00a..0000000000 --- a/modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp +++ /dev/null @@ -1,442 +0,0 @@ -/*************************************************************************/ -/* FBXBinaryTokenizer.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ -/** @file FBXBinaryTokenizer.cpp - * @brief Implementation of a fake lexer for binary fbx files - - * we emit tokens so the parser needs almost no special handling - * for binary files. - */ - -#include "ByteSwapper.h" -#include "FBXTokenizer.h" -#include "core/string/print_string.h" - -#include <stdint.h> - -namespace FBXDocParser { -// ------------------------------------------------------------------------------------------------ -Token::Token(const char *sbegin, const char *send, TokenType type, size_t offset) : - sbegin(sbegin), - send(send), - type(type), - line(offset), - column(BINARY_MARKER) { -#ifdef DEBUG_ENABLED - // contents is bad.. :/ - contents = std::string(sbegin, static_cast<size_t>(send - sbegin)); -#endif - // calc length - // measure from sBegin to sEnd and validate? -} - -namespace { - -// ------------------------------------------------------------------------------------------------ -// signal tokenization error -void TokenizeError(const std::string &message, size_t offset) { - print_error("[FBX-Tokenize] " + String(message.c_str()) + ", offset " + itos(offset)); -} - -// ------------------------------------------------------------------------------------------------ -size_t Offset(const char *begin, const char *cursor) { - //ai_assert(begin <= cursor); - - return cursor - begin; -} - -// ------------------------------------------------------------------------------------------------ -void TokenizeError(const std::string &message, const char *begin, const char *cursor) { - TokenizeError(message, Offset(begin, cursor)); -} - -// ------------------------------------------------------------------------------------------------ -uint32_t ReadWord(const char *input, const char *&cursor, const char *end) { - const size_t k_to_read = sizeof(uint32_t); - if (Offset(cursor, end) < k_to_read) { - TokenizeError("cannot ReadWord, out of bounds", input, cursor); - } - - uint32_t word; - ::memcpy(&word, cursor, 4); - AI_SWAP4(word); - - cursor += k_to_read; - - return word; -} - -// ------------------------------------------------------------------------------------------------ -uint64_t ReadDoubleWord(const char *input, const char *&cursor, const char *end) { - const size_t k_to_read = sizeof(uint64_t); - if (Offset(cursor, end) < k_to_read) { - TokenizeError("cannot ReadDoubleWord, out of bounds", input, cursor); - } - - uint64_t dword /*= *reinterpret_cast<const uint64_t*>(cursor)*/; - ::memcpy(&dword, cursor, sizeof(uint64_t)); - AI_SWAP8(dword); - - cursor += k_to_read; - - return dword; -} - -// ------------------------------------------------------------------------------------------------ -uint8_t ReadByte(const char *input, const char *&cursor, const char *end) { - if (Offset(cursor, end) < sizeof(uint8_t)) { - TokenizeError("cannot ReadByte, out of bounds", input, cursor); - } - - uint8_t word; /* = *reinterpret_cast< const uint8_t* >( cursor )*/ - ::memcpy(&word, cursor, sizeof(uint8_t)); - ++cursor; - - return word; -} - -// ------------------------------------------------------------------------------------------------ -unsigned int ReadString(const char *&sbegin_out, const char *&send_out, const char *input, - const char *&cursor, const char *end, bool long_length = false, bool allow_null = false) { - const uint32_t len_len = long_length ? 4 : 1; - if (Offset(cursor, end) < len_len) { - TokenizeError("cannot ReadString, out of bounds reading length", input, cursor); - } - - const uint32_t length = long_length ? ReadWord(input, cursor, end) : ReadByte(input, cursor, end); - - if (Offset(cursor, end) < length) { - TokenizeError("cannot ReadString, length is out of bounds", input, cursor); - } - - sbegin_out = cursor; - cursor += length; - - send_out = cursor; - - if (!allow_null) { - for (unsigned int i = 0; i < length; ++i) { - if (sbegin_out[i] == '\0') { - TokenizeError("failed ReadString, unexpected NUL character in string", input, cursor); - } - } - } - - return length; -} - -// ------------------------------------------------------------------------------------------------ -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; - sbegin_out = cursor++; - - switch (type) { - // 16 bit int - case 'Y': - cursor += 2; - break; - - // 1 bit bool flag (yes/no) - case 'C': - cursor += 1; - break; - - // 32 bit int - case 'I': - // <- fall through - - // float - case 'F': - cursor += 4; - break; - - // double - case 'D': - cursor += 8; - break; - - // 64 bit int - case 'L': - cursor += 8; - break; - - // note: do not write cursor += ReadWord(...cursor) as this would be UB - - // raw binary data - case 'R': { - const uint32_t length = ReadWord(input, cursor, end); - cursor += length; - break; - } - - case 'b': - // TODO: what is the 'b' type code? Right now we just skip over it / - // take the full range we could get - cursor = end; - break; - - // array of * - case 'f': - case 'd': - case 'l': - case 'i': - case 'c': { - const uint32_t length = ReadWord(input, cursor, end); - const uint32_t encoding = ReadWord(input, cursor, end); - - const uint32_t comp_len = ReadWord(input, cursor, end); - - // compute length based on type and check against the stored value - if (encoding == 0) { - uint32_t stride = 0; - switch (type) { - case 'f': - case 'i': - stride = 4; - break; - - case 'd': - case 'l': - stride = 8; - break; - - case 'c': - stride = 1; - break; - - default: - break; - }; - //ai_assert(stride > 0); - if (length * stride != comp_len) { - TokenizeError("cannot ReadData, calculated data stride differs from what the file claims", input, cursor); - } - } - // zip/deflate algorithm (encoding==1)? take given length. anything else? die - else if (encoding != 1) { - TokenizeError("cannot ReadData, unknown encoding", input, cursor); - } - cursor += comp_len; - break; - } // string - case 'S': { - const char *sb, *se; - // 0 characters can legally happen in such strings - ReadString(sb, se, input, cursor, end, true, true); - 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 - send_out = cursor; -} - -// ------------------------------------------------------------------------------------------------ -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); - - // we may get 0 if reading reached the end of the file - - // fbx files have a mysterious extra footer which I don't know - // how to extract any information from, but at least it always - // starts with a 0. - if (!end_offset) { - return false; - } - - 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 - const uint64_t prop_count = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end); - - // the third data word contains the length of the property list - 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 = nullptr, *send = nullptr; - ReadString(sbeg, send, input, cursor, end); - - output_tokens.push_back(new_Token(sbeg, send, TokenType_KEY, Offset(input, 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, corrupt); - if (corrupt) { - return false; - } - - output_tokens.push_back(new_Token(sbeg, send, TokenType_DATA, Offset(input, cursor))); - - if (i != prop_count - 1) { - output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_COMMA, Offset(input, 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 - // that the sub-scope exists (i.e. to distinguish between P: and P : {}) - // this NUL record is 13 bytes long on 32 bit version and 25 bytes long on 64 bit. - const size_t sentinel_block_length = is64bits ? (sizeof(uint64_t) * 3 + 1) : (sizeof(uint32_t) * 3 + 1); - - if (Offset(input, cursor) < end_offset) { - if (end_offset - Offset(input, cursor) < sentinel_block_length) { - TokenizeError("insufficient padding bytes at block end", input, cursor); - } - - output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_OPEN_BRACKET, Offset(input, 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, 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; - } - - if (Offset(input, cursor) != end_offset) { - TokenizeError("scope length not reached, something is wrong", input, cursor); - corrupt = true; - return false; - } - - return true; -} -} // anonymous namespace - -// ------------------------------------------------------------------------------------------------ -// 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, bool &corrupt) { - if (length < 0x1b) { - //TokenizeError("file is too short",0); - } - - if (strncmp(input, "Kaydara FBX Binary", 18)) { - TokenizeError("magic bytes not found", 0); - } - - const char *cursor = input + 18; - /*Result ignored*/ ReadByte(input, cursor, input + length); - /*Result ignored*/ ReadByte(input, cursor, input + length); - /*Result ignored*/ ReadByte(input, cursor, input + length); - /*Result ignored*/ ReadByte(input, cursor, input + length); - /*Result ignored*/ ReadByte(input, cursor, input + length); - const uint32_t version = ReadWord(input, cursor, input + length); - print_verbose("FBX Version: " + itos(version)); - //ASSIMP_LOG_DEBUG_F("FBX version: ", version); - const bool is64bits = version >= 7500; - const char *end = input + length; - while (cursor < end) { - if (!ReadScope(output_tokens, input, cursor, input + length, is64bits, corrupt)) { - break; - } - } -} -} // namespace FBXDocParser diff --git a/modules/fbx/fbx_parser/FBXCommon.h b/modules/fbx/fbx_parser/FBXCommon.h deleted file mode 100644 index 611bf22d3b..0000000000 --- a/modules/fbx/fbx_parser/FBXCommon.h +++ /dev/null @@ -1,110 +0,0 @@ -/*************************************************************************/ -/* FBXCommon.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXCommon.h - * Some useful constants and enums for dealing with FBX files. - */ -#ifndef FBX_COMMON_H -#define FBX_COMMON_H - -#include <string> - -namespace FBXDocParser { -const std::string NULL_RECORD = { // 13 null bytes - '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0' -}; // who knows why -const std::string SEPARATOR = { '\x00', '\x01' }; // for use inside strings -const std::string MAGIC_NODE_TAG = "_$AssimpFbx$"; // from import -const int64_t SECOND = 46186158000; // FBX's kTime unit - -// rotation order. We'll probably use EulerXYZ for everything -enum RotOrder { - RotOrder_EulerXYZ = 0, - RotOrder_EulerXZY, - RotOrder_EulerYZX, - RotOrder_EulerYXZ, - RotOrder_EulerZXY, - RotOrder_EulerZYX, - - RotOrder_SphericXYZ, - - RotOrder_MAX // end-of-enum sentinel -}; - -enum TransformInheritance { - Transform_RrSs = 0, - Transform_RSrs = 1, - Transform_Rrs = 2, - TransformInheritance_MAX // end-of-enum sentinel -}; -} // namespace FBXDocParser - -#endif // FBX_COMMON_H diff --git a/modules/fbx/fbx_parser/FBXDeformer.cpp b/modules/fbx/fbx_parser/FBXDeformer.cpp deleted file mode 100644 index 4220ba62a7..0000000000 --- a/modules/fbx/fbx_parser/FBXDeformer.cpp +++ /dev/null @@ -1,271 +0,0 @@ -/*************************************************************************/ -/* FBXDeformer.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXNoteAttribute.cpp - * @brief Assimp::FBX::NodeAttribute (and subclasses) implementation - */ - -#include "FBXDocument.h" -#include "FBXDocumentUtil.h" -#include "FBXMeshGeometry.h" -#include "FBXParser.h" -#include "core/math/math_funcs.h" -#include "core/math/transform_3d.h" - -#include <iostream> - -namespace FBXDocParser { - -using namespace Util; - -// ------------------------------------------------------------------------------------------------ -Deformer::Deformer(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : - Object(id, element, name) { -} - -// ------------------------------------------------------------------------------------------------ -Deformer::~Deformer() { -} - -Constraint::Constraint(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : - Object(id, element, name) { -} - -Constraint::~Constraint() { -} - -// ------------------------------------------------------------------------------------------------ -Cluster::Cluster(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : - Deformer(id, element, doc, name), valid_transformAssociateModel(false) { - const ScopePtr sc = GetRequiredScope(element); - // for( auto element : sc.Elements()) - // { - // std::cout << "cluster element: " << element.first << std::endl; - // } - // - // element: Indexes - // element: Transform - // element: TransformAssociateModel - // element: TransformLink - // element: UserData - // element: Version - // element: Weights - - const ElementPtr Indexes = sc->GetElement("Indexes"); - const ElementPtr Weights = sc->GetElement("Weights"); - - const ElementPtr TransformAssociateModel = sc->GetElement("TransformAssociateModel"); - if (TransformAssociateModel != nullptr) { - //Transform t = ReadMatrix(*TransformAssociateModel); - link_mode = SkinLinkMode_Additive; - valid_transformAssociateModel = true; - } else { - link_mode = SkinLinkMode_Normalized; - valid_transformAssociateModel = false; - } - - const ElementPtr Transform = GetRequiredElement(sc, "Transform", element); - const ElementPtr TransformLink = GetRequiredElement(sc, "TransformLink", element); - - // todo: check if we need this - //const Element& TransformAssociateModel = GetRequiredElement(sc, "TransformAssociateModel", &element); - - transform = ReadMatrix(Transform); - transformLink = ReadMatrix(TransformLink); - - // it is actually possible that there be Deformer's with no weights - if (!!Indexes != !!Weights) { - DOMError("either Indexes or Weights are missing from Cluster", element); - } - - if (Indexes) { - ParseVectorDataArray(indices, Indexes); - ParseVectorDataArray(weights, Weights); - } - - if (indices.size() != weights.size()) { - DOMError("sizes of index and weight array don't match up", element); - } - - // read assigned node - const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Model"); - for (const Connection *con : conns) { - const Model *mod = ProcessSimpleConnection<Model>(*con, false, "Model -> Cluster", element); - if (mod) { - node = mod; - break; - } - } - - if (!node) { - DOMError("failed to read target Node for Cluster", element); - node = nullptr; - } -} - -// ------------------------------------------------------------------------------------------------ -Cluster::~Cluster() { -} - -// ------------------------------------------------------------------------------------------------ -Skin::Skin(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : - Deformer(id, element, doc, name), accuracy(0.0f) { - const ScopePtr sc = GetRequiredScope(element); - - // keep this it is used for debugging and any FBX format changes - // for (auto element : sc.Elements()) { - // std::cout << "skin element: " << element.first << std::endl; - // } - - const ElementPtr Link_DeformAcuracy = sc->GetElement("Link_DeformAcuracy"); - if (Link_DeformAcuracy) { - accuracy = ParseTokenAsFloat(GetRequiredToken(Link_DeformAcuracy, 0)); - } - - const ElementPtr SkinType = sc->GetElement("SkinningType"); - - if (SkinType) { - std::string skin_type = ParseTokenAsString(GetRequiredToken(SkinType, 0)); - - if (skin_type == "Linear") { - skinType = Skin_Linear; - } else if (skin_type == "Rigid") { - skinType = Skin_Rigid; - } else if (skin_type == "DualQuaternion") { - skinType = Skin_DualQuaternion; - } else if (skin_type == "Blend") { - skinType = Skin_Blend; - } else { - print_error("[doc:skin] could not find valid skin type: " + String(skin_type.c_str())); - } - } - - // resolve assigned clusters - const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Deformer"); - - // - - clusters.reserve(conns.size()); - for (const Connection *con : conns) { - const Cluster *cluster = ProcessSimpleConnection<Cluster>(*con, false, "Cluster -> Skin", element); - if (cluster) { - clusters.push_back(cluster); - continue; - } - } -} - -// ------------------------------------------------------------------------------------------------ -Skin::~Skin() { -} -// ------------------------------------------------------------------------------------------------ -BlendShape::BlendShape(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : - Deformer(id, element, doc, name) { - const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Deformer"); - blendShapeChannels.reserve(conns.size()); - for (const Connection *con : conns) { - const BlendShapeChannel *bspc = ProcessSimpleConnection<BlendShapeChannel>(*con, false, "BlendShapeChannel -> BlendShape", element); - if (bspc) { - blendShapeChannels.push_back(bspc); - continue; - } - } -} -// ------------------------------------------------------------------------------------------------ -BlendShape::~BlendShape() { -} -// ------------------------------------------------------------------------------------------------ -BlendShapeChannel::BlendShapeChannel(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : - Deformer(id, element, doc, name) { - const ScopePtr sc = GetRequiredScope(element); - const ElementPtr DeformPercent = sc->GetElement("DeformPercent"); - if (DeformPercent) { - percent = ParseTokenAsFloat(GetRequiredToken(DeformPercent, 0)); - } - const ElementPtr FullWeights = sc->GetElement("FullWeights"); - if (FullWeights) { - ParseVectorDataArray(fullWeights, FullWeights); - } - const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Geometry"); - shapeGeometries.reserve(conns.size()); - for (const Connection *con : conns) { - const ShapeGeometry *const sg = ProcessSimpleConnection<ShapeGeometry>(*con, false, "Shape -> BlendShapeChannel", element); - if (sg) { - shapeGeometries.push_back(sg); - continue; - } - } -} -// ------------------------------------------------------------------------------------------------ -BlendShapeChannel::~BlendShapeChannel() { -} -// ------------------------------------------------------------------------------------------------ -} // namespace FBXDocParser diff --git a/modules/fbx/fbx_parser/FBXDocument.cpp b/modules/fbx/fbx_parser/FBXDocument.cpp deleted file mode 100644 index 92c62e68b5..0000000000 --- a/modules/fbx/fbx_parser/FBXDocument.cpp +++ /dev/null @@ -1,636 +0,0 @@ -/*************************************************************************/ -/* FBXDocument.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the* - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXDocument.cpp - * @brief Implementation of the FBX DOM classes - */ - -#include "FBXDocument.h" -#include "FBXDocumentUtil.h" -#include "FBXImportSettings.h" -#include "FBXMeshGeometry.h" -#include "FBXParser.h" -#include "FBXProperties.h" -#include "FBXUtil.h" - -#include <algorithm> -#include <functional> -#include <iostream> -#include <map> -#include <memory> - -namespace FBXDocParser { - -using namespace Util; - -// ------------------------------------------------------------------------------------------------ -LazyObject::LazyObject(uint64_t id, const ElementPtr element, const Document &doc) : - doc(doc), element(element), id(id) { - // empty -} - -// ------------------------------------------------------------------------------------------------ -LazyObject::~LazyObject() { - object.reset(); -} - -ObjectPtr LazyObject::LoadObject() { - if (IsBeingConstructed() || FailedToConstruct()) { - return nullptr; - } - - if (object) { - return object.get(); - } - - TokenPtr key = element->KeyToken(); - ERR_FAIL_COND_V(!key, nullptr); - const TokenList &tokens = element->Tokens(); - - if (tokens.size() < 3) { - //DOMError("expected at least 3 tokens: id, name and class tag",&element); - return nullptr; - } - - const char *err = nullptr; - std::string name = ParseTokenAsString(tokens[1], err); - if (err) { - DOMError(err, element); - } - - // small fix for binary reading: binary fbx files don't use - // prefixes such as Model:: in front of their names. The - // loading code expects this at many places, though! - // so convert the binary representation (a 0x0001) to the - // double colon notation. - if (tokens[1]->IsBinary()) { - for (size_t i = 0; i < name.length(); ++i) { - if (name[i] == 0x0 && name[i + 1] == 0x1) { - name = name.substr(i + 2) + "::" + name.substr(0, i); - } - } - } - - const std::string classtag = ParseTokenAsString(tokens[2], err); - if (err) { - DOMError(err, element); - } - - // prevent recursive calls - flags |= BEING_CONSTRUCTED; - - // this needs to be relatively fast since it happens a lot, - // so avoid constructing strings all the time. - const char *obtype = key->begin(); - const size_t length = static_cast<size_t>(key->end() - key->begin()); - - if (!strncmp(obtype, "Pose", length)) { - object.reset(new FbxPose(id, element, doc, name)); - } else if (!strncmp(obtype, "Geometry", length)) { - if (!strcmp(classtag.c_str(), "Mesh")) { - object.reset(new MeshGeometry(id, element, name, doc)); - } - if (!strcmp(classtag.c_str(), "Shape")) { - object.reset(new ShapeGeometry(id, element, name, doc)); - } - if (!strcmp(classtag.c_str(), "Line")) { - object.reset(new LineGeometry(id, element, name, doc)); - } - } else if (!strncmp(obtype, "NodeAttribute", length)) { - if (!strcmp(classtag.c_str(), "Camera")) { - object.reset(new Camera(id, element, doc, name)); - } else if (!strcmp(classtag.c_str(), "CameraSwitcher")) { - object.reset(new CameraSwitcher(id, element, doc, name)); - } else if (!strcmp(classtag.c_str(), "Light")) { - object.reset(new Light(id, element, doc, name)); - } else if (!strcmp(classtag.c_str(), "Null")) { - object.reset(new Null(id, element, doc, name)); - } else if (!strcmp(classtag.c_str(), "LimbNode")) { - // This is an older format for bones - // this is what blender uses I believe - object.reset(new LimbNode(id, element, doc, name)); - } - } else if (!strncmp(obtype, "Constraint", length)) { - object.reset(new Constraint(id, element, doc, name)); - } else if (!strncmp(obtype, "Deformer", length)) { - if (!strcmp(classtag.c_str(), "Cluster")) { - object.reset(new Cluster(id, element, doc, name)); - } else if (!strcmp(classtag.c_str(), "Skin")) { - object.reset(new Skin(id, element, doc, name)); - } else if (!strcmp(classtag.c_str(), "BlendShape")) { - object.reset(new BlendShape(id, element, doc, name)); - } else if (!strcmp(classtag.c_str(), "BlendShapeChannel")) { - object.reset(new BlendShapeChannel(id, element, doc, name)); - } - } else if (!strncmp(obtype, "Model", length)) { - // Model is normal node - - // LimbNode model is a 'bone' node. - if (!strcmp(classtag.c_str(), "LimbNode")) { - object.reset(new ModelLimbNode(id, element, doc, name)); - - } else if (strcmp(classtag.c_str(), "IKEffector") && strcmp(classtag.c_str(), "FKEffector")) { - // FK and IK effectors are not supported. - object.reset(new Model(id, element, doc, name)); - } - } else if (!strncmp(obtype, "Material", length)) { - object.reset(new Material(id, element, doc, name)); - } else if (!strncmp(obtype, "Texture", length)) { - object.reset(new Texture(id, element, doc, name)); - } else if (!strncmp(obtype, "LayeredTexture", length)) { - object.reset(new LayeredTexture(id, element, doc, name)); - } else if (!strncmp(obtype, "Video", length)) { - object.reset(new Video(id, element, doc, name)); - } else if (!strncmp(obtype, "AnimationStack", length)) { - object.reset(new AnimationStack(id, element, name, doc)); - } else if (!strncmp(obtype, "AnimationLayer", length)) { - object.reset(new AnimationLayer(id, element, name, doc)); - } else if (!strncmp(obtype, "AnimationCurve", length)) { - object.reset(new AnimationCurve(id, element, name, doc)); - } else if (!strncmp(obtype, "AnimationCurveNode", length)) { - object.reset(new AnimationCurveNode(id, element, name, doc)); - } else { - ERR_FAIL_V_MSG(nullptr, "FBX contains unsupported object: " + String(obtype)); - } - - flags &= ~BEING_CONSTRUCTED; - - return object.get(); -} - -// ------------------------------------------------------------------------------------------------ -Object::Object(uint64_t id, const ElementPtr element, const std::string &name) : - PropertyTable(element), element(element), name(name), id(id) { -} - -// ------------------------------------------------------------------------------------------------ -Object::~Object() { - // empty -} - -// ------------------------------------------------------------------------------------------------ -FileGlobalSettings::FileGlobalSettings(const Document &doc) : - PropertyTable(), doc(doc) { - // empty -} - -// ------------------------------------------------------------------------------------------------ -FileGlobalSettings::~FileGlobalSettings() { -} - -// ------------------------------------------------------------------------------------------------ -Document::Document(const Parser &parser, const ImportSettings &settings) : - settings(settings), parser(parser) { - // Cannot use array default initialization syntax because vc8 fails on it - for (unsigned int &timeStamp : creationTimeStamp) { - timeStamp = 0; - } - - // we must check if we can read the header version safely, if its outdated then drop it. - if (ReadHeader()) { - SafeToImport = true; - ReadPropertyTemplates(); - - ReadGlobalSettings(); - - // This order is important, connections need parsed objects to check - // whether connections are ok or not. Objects may not be evaluated yet, - // though, since this may require valid connections. - ReadObjects(); - ReadConnections(); - } -} - -// ------------------------------------------------------------------------------------------------ -Document::~Document() { - for (PropertyTemplateMap::value_type v : templates) { - delete v.second; - } - - for (ObjectMap::value_type &v : objects) { - delete v.second; - } - - for (ConnectionMap::value_type &v : src_connections) { - delete v.second; - } - - // clear globals import pointer - globals.reset(); -} - -// ------------------------------------------------------------------------------------------------ -static const unsigned int LowerSupportedVersion = 7100; -static const unsigned int UpperSupportedVersion = 7700; - -bool Document::ReadHeader() { - // Read ID objects from "Objects" section - ScopePtr sc = parser.GetRootScope(); - ElementPtr ehead = sc->GetElement("FBXHeaderExtension"); - if (!ehead || !ehead->Compound()) { - 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)); - - // While we may have some success with newer files, we don't support - // the older 6.n fbx format - if (fbxVersion < LowerSupportedVersion) { - DOMWarning("unsupported, old format version, FBX 2015-2020, you must re-export in a more modern version of your original modelling application"); - return false; - } - if (fbxVersion > UpperSupportedVersion) { - DOMWarning("unsupported, newer format version, supported are only FBX 2015, up to FBX 2020" - " trying to read it nevertheless"); - } - - const ElementPtr ecreator = shead->GetElement("Creator"); - if (ecreator) { - creator = ParseTokenAsString(GetRequiredToken(ecreator, 0)); - } - - // Scene Info - const ElementPtr scene_info = shead->GetElement("SceneInfo"); - - if (scene_info) { - metadata_properties.Setup(scene_info); - } - - const ElementPtr etimestamp = shead->GetElement("CreationTimeStamp"); - if (etimestamp && etimestamp->Compound()) { - const ScopePtr stimestamp = etimestamp->Compound(); - creationTimeStamp[0] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Year"), 0)); - creationTimeStamp[1] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Month"), 0)); - creationTimeStamp[2] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Day"), 0)); - creationTimeStamp[3] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Hour"), 0)); - creationTimeStamp[4] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Minute"), 0)); - creationTimeStamp[5] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Second"), 0)); - creationTimeStamp[6] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Millisecond"), 0)); - } - - return true; -} - -// ------------------------------------------------------------------------------------------------ -void Document::ReadGlobalSettings() { - ERR_FAIL_COND_MSG(globals != nullptr, "Global settings is already setup this is a serious error and should be reported"); - - globals = std::make_shared<FileGlobalSettings>(*this); -} - -// ------------------------------------------------------------------------------------------------ -void Document::ReadObjects() { - // read ID objects from "Objects" section - const ScopePtr sc = parser.GetRootScope(); - const ElementPtr eobjects = sc->GetElement("Objects"); - if (!eobjects || !eobjects->Compound()) { - DOMError("no Objects dictionary found"); - } - - // add a dummy entry to represent the Model::RootNode object (id 0), - // which is only indirectly defined in the input file - objects[0] = new LazyObject(0L, eobjects, *this); - - const ScopePtr sobjects = eobjects->Compound(); - for (const ElementMap::value_type &iter : sobjects->Elements()) { - // extract ID - const TokenList &tok = iter.second->Tokens(); - - if (tok.empty()) { - DOMError("expected ID after object key", iter.second); - } - - const char *err; - const uint64_t id = ParseTokenAsID(tok[0], err); - if (err) { - DOMError(err, iter.second); - } - - // id=0 is normally implicit - if (id == 0L) { - DOMError("encountered object with implicitly defined id 0", iter.second); - } - - if (objects.find(id) != objects.end()) { - DOMWarning("encountered duplicate object id, ignoring first occurrence", iter.second); - } - - objects[id] = new LazyObject(id, iter.second, *this); - - // grab all animation stacks upfront since there is no listing of them - if (!strcmp(iter.first.c_str(), "AnimationStack")) { - animationStacks.push_back(id); - } else if (!strcmp(iter.first.c_str(), "Constraint")) { - constraints.push_back(id); - } else if (!strcmp(iter.first.c_str(), "Pose")) { - bind_poses.push_back(id); - } else if (!strcmp(iter.first.c_str(), "Material")) { - materials.push_back(id); - } else if (!strcmp(iter.first.c_str(), "Deformer")) { - TokenPtr key = iter.second->KeyToken(); - ERR_CONTINUE_MSG(!key, "[parser bug] invalid token key for deformer"); - const TokenList &tokens = iter.second->Tokens(); - const std::string class_tag = ParseTokenAsString(tokens[2], err); - - if (err) { - DOMError(err, iter.second); - } - - if (class_tag == "Skin") { - //print_verbose("registered skin:" + itos(id)); - skins.push_back(id); - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -void Document::ReadPropertyTemplates() { -} - -// ------------------------------------------------------------------------------------------------ -void Document::ReadConnections() { - const ScopePtr sc = parser.GetRootScope(); - - // read property templates from "Definitions" section - const ElementPtr econns = sc->GetElement("Connections"); - if (!econns || !econns->Compound()) { - DOMError("no Connections dictionary found"); - } - - uint64_t insertionOrder = 0l; - const ScopePtr sconns = econns->Compound(); - const ElementCollection conns = sconns->GetCollection("C"); - for (ElementMap::const_iterator it = conns.first; it != conns.second; ++it) { - const ElementPtr el = (*it).second; - const std::string &type = ParseTokenAsString(GetRequiredToken(el, 0)); - - // PP = property-property connection, ignored for now - // (tokens: "PP", ID1, "Property1", ID2, "Property2") - if (type == "PP") { - continue; - } - - const uint64_t src = ParseTokenAsID(GetRequiredToken(el, 1)); - const uint64_t dest = ParseTokenAsID(GetRequiredToken(el, 2)); - - // OO = object-object connection - // OP = object-property connection, in which case the destination property follows the object ID - const std::string &prop = (type == "OP" ? ParseTokenAsString(GetRequiredToken(el, 3)) : ""); - - if (objects.find(src) == objects.end()) { - DOMWarning("source object for connection does not exist", el); - continue; - } - - // dest may be 0 (root node) but we added a dummy object before - if (objects.find(dest) == objects.end()) { - DOMWarning("destination object for connection does not exist", el); - continue; - } - - // add new connection - const Connection *const c = new Connection(insertionOrder++, src, dest, prop, *this); - src_connections.insert(ConnectionMap::value_type(src, c)); - dest_connections.insert(ConnectionMap::value_type(dest, c)); - } -} - -// ------------------------------------------------------------------------------------------------ -const std::vector<const AnimationStack *> &Document::AnimationStacks() const { - if (!animationStacksResolved.empty() || animationStacks.empty()) { - return animationStacksResolved; - } - - animationStacksResolved.reserve(animationStacks.size()); - for (uint64_t id : animationStacks) { - LazyObject *lazy = GetObject(id); - - // Two things happen here: - // We cast internally an Object PTR to an Animation Stack PTR - // We return invalid weak_ptrs for objects which are invalid - - const AnimationStack *stack = lazy->Get<AnimationStack>(); - ERR_CONTINUE_MSG(!stack, "invalid ptr to AnimationStack - conversion failure"); - - // We push back the weak reference :) to keep things simple, as ownership is on the parser side so it won't be cleaned up. - animationStacksResolved.push_back(stack); - } - - return animationStacksResolved; -} - -// ------------------------------------------------------------------------------------------------ -LazyObject *Document::GetObject(uint64_t id) const { - ObjectMap::const_iterator it = objects.find(id); - return it == objects.end() ? nullptr : (*it).second; -} - -#define MAX_CLASSNAMES 6 - -// ------------------------------------------------------------------------------------------------ -std::vector<const Connection *> Document::GetConnectionsSequenced(uint64_t id, const ConnectionMap &conns) const { - std::vector<const Connection *> temp; - - const std::pair<ConnectionMap::const_iterator, ConnectionMap::const_iterator> range = - conns.equal_range(id); - - temp.reserve(std::distance(range.first, range.second)); - for (ConnectionMap::const_iterator it = range.first; it != range.second; ++it) { - temp.push_back((*it).second); - } - - std::sort(temp.begin(), temp.end(), std::mem_fn(&Connection::Compare)); - - return temp; // NRVO should handle this -} - -// ------------------------------------------------------------------------------------------------ -std::vector<const Connection *> Document::GetConnectionsSequenced(uint64_t id, bool is_src, - const ConnectionMap &conns, - const char *const *classnames, - size_t count) const - -{ - size_t lengths[MAX_CLASSNAMES]; - - const size_t c = count; - for (size_t i = 0; i < c; ++i) { - lengths[i] = strlen(classnames[i]); - } - - std::vector<const Connection *> temp; - const std::pair<ConnectionMap::const_iterator, ConnectionMap::const_iterator> range = - conns.equal_range(id); - - temp.reserve(std::distance(range.first, range.second)); - for (ConnectionMap::const_iterator it = range.first; it != range.second; ++it) { - TokenPtr key = (is_src ? (*it).second->LazyDestinationObject() : (*it).second->LazySourceObject())->GetElement()->KeyToken(); - - const char *obtype = key->begin(); - - for (size_t i = 0; i < c; ++i) { - //ai_assert(classnames[i]); - if (static_cast<size_t>(std::distance(key->begin(), key->end())) == lengths[i] && !strncmp(classnames[i], obtype, lengths[i])) { - obtype = nullptr; - break; - } - } - - if (obtype) { - continue; - } - - temp.push_back((*it).second); - } - - std::sort(temp.begin(), temp.end(), std::mem_fn(&Connection::Compare)); - return temp; // NRVO should handle this -} - -// ------------------------------------------------------------------------------------------------ -std::vector<const Connection *> Document::GetConnectionsBySourceSequenced(uint64_t source) const { - return GetConnectionsSequenced(source, ConnectionsBySource()); -} - -// ------------------------------------------------------------------------------------------------ -std::vector<const Connection *> Document::GetConnectionsBySourceSequenced(uint64_t src, const char *classname) const { - const char *arr[] = { classname }; - return GetConnectionsBySourceSequenced(src, arr, 1); -} - -// ------------------------------------------------------------------------------------------------ -std::vector<const Connection *> Document::GetConnectionsBySourceSequenced(uint64_t source, - const char *const *classnames, size_t count) const { - return GetConnectionsSequenced(source, true, ConnectionsBySource(), classnames, count); -} - -// ------------------------------------------------------------------------------------------------ -std::vector<const Connection *> Document::GetConnectionsByDestinationSequenced(uint64_t dest, - const char *classname) const { - const char *arr[] = { classname }; - return GetConnectionsByDestinationSequenced(dest, arr, 1); -} - -// ------------------------------------------------------------------------------------------------ -std::vector<const Connection *> Document::GetConnectionsByDestinationSequenced(uint64_t dest) const { - return GetConnectionsSequenced(dest, ConnectionsByDestination()); -} - -// ------------------------------------------------------------------------------------------------ -std::vector<const Connection *> Document::GetConnectionsByDestinationSequenced(uint64_t dest, - const char *const *classnames, size_t count) const { - return GetConnectionsSequenced(dest, false, ConnectionsByDestination(), classnames, count); -} - -// ------------------------------------------------------------------------------------------------ -Connection::Connection(uint64_t insertionOrder, uint64_t src, uint64_t dest, const std::string &prop, - const Document &doc) : - insertionOrder(insertionOrder), prop(prop), src(src), dest(dest), doc(doc) { -} - -// ------------------------------------------------------------------------------------------------ -Connection::~Connection() { - // empty -} - -// ------------------------------------------------------------------------------------------------ -LazyObject *Connection::LazySourceObject() const { - LazyObject *const lazy = doc.GetObject(src); - return lazy; -} - -// ------------------------------------------------------------------------------------------------ -LazyObject *Connection::LazyDestinationObject() const { - LazyObject *const lazy = doc.GetObject(dest); - return lazy; -} - -// ------------------------------------------------------------------------------------------------ -Object *Connection::SourceObject() const { - LazyObject *lazy = doc.GetObject(src); - //ai_assert(lazy); - return lazy->LoadObject(); -} - -// ------------------------------------------------------------------------------------------------ -Object *Connection::DestinationObject() const { - LazyObject *lazy = doc.GetObject(dest); - //ai_assert(lazy); - return lazy->LoadObject(); -} -} // namespace FBXDocParser diff --git a/modules/fbx/fbx_parser/FBXDocument.h b/modules/fbx/fbx_parser/FBXDocument.h deleted file mode 100644 index 539d633331..0000000000 --- a/modules/fbx/fbx_parser/FBXDocument.h +++ /dev/null @@ -1,1252 +0,0 @@ -/*************************************************************************/ -/* FBXDocument.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -/** @file FBXDocument.h - * @brief FBX DOM - */ -#ifndef FBX_DOCUMENT_H -#define FBX_DOCUMENT_H - -#include "FBXCommon.h" -#include "FBXParser.h" -#include "FBXProperties.h" -#include "core/math/transform_3d.h" -#include "core/math/vector2.h" -#include "core/math/vector3.h" -#include "core/string/print_string.h" -#include <stdint.h> -#include <numeric> - -#define _AI_CONCAT(a, b) a##b -#define AI_CONCAT(a, b) _AI_CONCAT(a, b) - -namespace FBXDocParser { - -class Parser; -class Object; -struct ImportSettings; -class Connection; - -class PropertyTable; -class Document; -class Material; -class ShapeGeometry; -class LineGeometry; -class Geometry; - -class Video; - -class AnimationCurve; -class AnimationCurveNode; -class AnimationLayer; -class AnimationStack; - -class BlendShapeChannel; -class BlendShape; -class Skin; -class Cluster; - -typedef Object *ObjectPtr; -#define new_Object new Object - -/** Represents a delay-parsed FBX objects. Many objects in the scene - * are not needed by assimp, so it makes no sense to parse them - * upfront. */ -class LazyObject { -public: - LazyObject(uint64_t id, const ElementPtr element, const Document &doc); - ~LazyObject(); - - ObjectPtr LoadObject(); - - /* Casting weak pointers to their templated type safely and preserving ref counting and safety - * with lock() keyword to prevent leaking memory - */ - template <typename T> - const T *Get() { - ObjectPtr ob = LoadObject(); - return dynamic_cast<const T *>(ob); - } - - uint64_t ID() const { - return id; - } - - bool IsBeingConstructed() const { - return (flags & BEING_CONSTRUCTED) != 0; - } - - bool FailedToConstruct() const { - return (flags & FAILED_TO_CONSTRUCT) != 0; - } - - ElementPtr GetElement() const { - return element; - } - - const Document &GetDocument() const { - return doc; - } - -private: - const Document &doc; - ElementPtr element = nullptr; - std::shared_ptr<Object> object = nullptr; - const uint64_t id = 0; - - enum Flags { - BEING_CONSTRUCTED = 0x1, - FAILED_TO_CONSTRUCT = 0x2 - }; - - unsigned int flags = 0; -}; - -/** Base class for in-memory (DOM) representations of FBX objects */ -class Object : public PropertyTable { -public: - Object(uint64_t id, const ElementPtr element, const std::string &name); - - virtual ~Object(); - - ElementPtr SourceElement() const { - return element; - } - - const std::string &Name() const { - return name; - } - - uint64_t ID() const { - return id; - } - -protected: - const ElementPtr element = nullptr; - const std::string name; - const uint64_t id; -}; - -/** DOM class for generic FBX NoteAttribute blocks. NoteAttribute's just hold a property table, - * fixed members are added by deriving classes. */ -class NodeAttribute : public Object { -public: - NodeAttribute(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); - virtual ~NodeAttribute(); -}; - -/** 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 { - return cameraId; - } - - const std::string &CameraName() const { - return cameraName; - } - - const std::string &CameraIndexName() const { - return cameraIndexName; - } - -private: - 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>(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>(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; -class FbxPose : public Object { -public: - FbxPose(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); - - const std::vector<FbxPoseNode *> &GetBindPoses() const { - return pose_nodes; - } - - virtual ~FbxPose(); - -private: - std::vector<FbxPoseNode *> pose_nodes; -}; - -class FbxPoseNode { -public: - FbxPoseNode(const ElementPtr element, const Document &doc, const std::string &name) { - const ScopePtr sc = GetRequiredScope(element); - - // get pose node transform - const ElementPtr Transform = GetRequiredElement(sc, "Matrix", element); - transform = ReadMatrix(Transform); - - // get node id this pose node is for - const ElementPtr NodeId = sc->GetElement("Node3D"); - if (NodeId) { - target_id = ParseTokenAsInt64(GetRequiredToken(NodeId, 0)); - } - - print_verbose("added posenode " + itos(target_id) + " transform: " + transform); - } - virtual ~FbxPoseNode() { - } - - uint64_t GetNodeID() const { - return target_id; - } - - Transform3D GetBindPose() const { - return transform; - } - -private: - uint64_t target_id = 0; - Transform3D transform; -}; - -/** DOM base class for FBX cameras attached to a node */ -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)); - fbx_simple_property(UpVector, Vector3, Vector3(0, 1, 0)); - fbx_simple_property(InterestPosition, Vector3, Vector3(0, 0, 0)); - - fbx_simple_property(AspectWidth, float, 1.0f); - fbx_simple_property(AspectHeight, float, 1.0f); - fbx_simple_property(FilmWidth, float, 1.0f); - fbx_simple_property(FilmHeight, float, 1.0f); - - fbx_simple_property(NearPlane, float, 0.1f); - fbx_simple_property(FarPlane, float, 100.0f); - - fbx_simple_property(FilmAspectRatio, float, 1.0f); - fbx_simple_property(ApertureMode, int, 0); - - fbx_simple_property(FieldOfView, float, 1.0f); - fbx_simple_property(FocalLength, float, 1.0f); -}; - -/** DOM base class for FBX null markers attached to a node */ -class Null : public NodeAttribute { -public: - Null(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); - virtual ~Null(); -}; - -/** DOM base class for FBX limb node markers attached to a node */ -class LimbNode : public NodeAttribute { -public: - LimbNode(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); - virtual ~LimbNode(); -}; - -/** DOM base class for FBX lights attached to a node */ -class Light : public NodeAttribute { -public: - Light(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); - virtual ~Light(); - - enum Type { - Type_Point, - Type_Directional, - Type_Spot, - Type_Area, - Type_Volume, - - Type_MAX // end-of-enum sentinel - }; - - enum Decay { - Decay_None, - Decay_Linear, - Decay_Quadratic, - Decay_Cubic, - - Decay_MAX // end-of-enum sentinel - }; - - fbx_simple_property(Color, Vector3, Vector3(1, 1, 1)); - fbx_simple_enum_property(LightType, Type, 0); - fbx_simple_property(CastLightOnObject, bool, false); - fbx_simple_property(DrawVolumetricLight, bool, true); - fbx_simple_property(DrawGroundProjection, bool, true); - fbx_simple_property(DrawFrontFacingVolumetricLight, bool, false); - fbx_simple_property(Intensity, float, 100.0f); - fbx_simple_property(InnerAngle, float, 0.0f); - fbx_simple_property(OuterAngle, float, 45.0f); - fbx_simple_property(Fog, int, 50); - fbx_simple_enum_property(DecayType, Decay, 2); - fbx_simple_property(DecayStart, float, 1.0f); - fbx_simple_property(FileName, std::string, ""); - - fbx_simple_property(EnableNearAttenuation, bool, false); - fbx_simple_property(NearAttenuationStart, float, 0.0f); - fbx_simple_property(NearAttenuationEnd, float, 0.0f); - fbx_simple_property(EnableFarAttenuation, bool, false); - fbx_simple_property(FarAttenuationStart, float, 0.0f); - fbx_simple_property(FarAttenuationEnd, float, 0.0f); - - fbx_simple_property(CastShadows, bool, true); - fbx_simple_property(ShadowColor, Vector3, Vector3(0, 0, 0)); - - fbx_simple_property(AreaLightShape, int, 0); - - fbx_simple_property(LeftBarnDoor, float, 20.0f); - fbx_simple_property(RightBarnDoor, float, 20.0f); - fbx_simple_property(TopBarnDoor, float, 20.0f); - fbx_simple_property(BottomBarnDoor, float, 20.0f); - fbx_simple_property(EnableBarnDoor, bool, true); -}; - -class Model; - -typedef Model *ModelPtr; -#define new_Model new Model - -/** DOM base class for FBX models (even though its semantics are more "node" than "model" */ -class Model : public Object { -public: - enum RotOrder { - RotOrder_EulerXYZ = 0, - RotOrder_EulerXZY, - RotOrder_EulerYZX, - RotOrder_EulerYXZ, - RotOrder_EulerZXY, - RotOrder_EulerZYX, - - RotOrder_SphericXYZ, - - RotOrder_MAX // end-of-enum sentinel - }; - - Model(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); - virtual ~Model(); - - fbx_simple_property(QuaternionInterpolate, int, 0); - - fbx_simple_property(RotationOffset, Vector3, Vector3()); - fbx_simple_property(RotationPivot, Vector3, Vector3()); - fbx_simple_property(ScalingOffset, Vector3, Vector3()); - fbx_simple_property(ScalingPivot, Vector3, Vector3()); - fbx_simple_property(TranslationActive, bool, false); - fbx_simple_property(TranslationMin, Vector3, Vector3()); - fbx_simple_property(TranslationMax, Vector3, Vector3()); - - fbx_simple_property(TranslationMinX, bool, false); - fbx_simple_property(TranslationMaxX, bool, false); - fbx_simple_property(TranslationMinY, bool, false); - fbx_simple_property(TranslationMaxY, bool, false); - fbx_simple_property(TranslationMinZ, bool, false); - fbx_simple_property(TranslationMaxZ, bool, false); - - fbx_simple_enum_property(RotationOrder, RotOrder, 0); - fbx_simple_property(RotationSpaceForLimitOnly, bool, false); - fbx_simple_property(RotationStiffnessX, float, 0.0f); - fbx_simple_property(RotationStiffnessY, float, 0.0f); - fbx_simple_property(RotationStiffnessZ, float, 0.0f); - fbx_simple_property(AxisLen, float, 0.0f); - - fbx_simple_property(PreRotation, Vector3, Vector3()); - fbx_simple_property(PostRotation, Vector3, Vector3()); - fbx_simple_property(RotationActive, bool, false); - - fbx_simple_property(RotationMin, Vector3, Vector3()); - fbx_simple_property(RotationMax, Vector3, Vector3()); - - fbx_simple_property(RotationMinX, bool, false); - fbx_simple_property(RotationMaxX, bool, false); - fbx_simple_property(RotationMinY, bool, false); - fbx_simple_property(RotationMaxY, bool, false); - fbx_simple_property(RotationMinZ, bool, false); - fbx_simple_property(RotationMaxZ, bool, false); - fbx_simple_enum_property(InheritType, TransformInheritance, 0); - - fbx_simple_property(ScalingActive, bool, false); - fbx_simple_property(ScalingMin, Vector3, Vector3()); - fbx_simple_property(ScalingMax, Vector3, Vector3(1, 1, 1)); - fbx_simple_property(ScalingMinX, bool, false); - fbx_simple_property(ScalingMaxX, bool, false); - fbx_simple_property(ScalingMinY, bool, false); - fbx_simple_property(ScalingMaxY, bool, false); - fbx_simple_property(ScalingMinZ, bool, false); - fbx_simple_property(ScalingMaxZ, bool, false); - - fbx_simple_property(GeometricTranslation, Vector3, Vector3()); - fbx_simple_property(GeometricRotation, Vector3, Vector3()); - fbx_simple_property(GeometricScaling, Vector3, Vector3(1, 1, 1)); - - fbx_simple_property(MinDampRangeX, float, 0.0f); - fbx_simple_property(MinDampRangeY, float, 0.0f); - fbx_simple_property(MinDampRangeZ, float, 0.0f); - fbx_simple_property(MaxDampRangeX, float, 0.0f); - fbx_simple_property(MaxDampRangeY, float, 0.0f); - fbx_simple_property(MaxDampRangeZ, float, 0.0f); - - fbx_simple_property(MinDampStrengthX, float, 0.0f); - fbx_simple_property(MinDampStrengthY, float, 0.0f); - fbx_simple_property(MinDampStrengthZ, float, 0.0f); - fbx_simple_property(MaxDampStrengthX, float, 0.0f); - fbx_simple_property(MaxDampStrengthY, float, 0.0f); - fbx_simple_property(MaxDampStrengthZ, float, 0.0f); - - fbx_simple_property(PreferredAngleX, float, 0.0f); - fbx_simple_property(PreferredAngleY, float, 0.0f); - fbx_simple_property(PreferredAngleZ, float, 0.0f); - - fbx_simple_property(Show, bool, true); - fbx_simple_property(LODBox, bool, false); - fbx_simple_property(Freeze, bool, false); - - const std::string &Shading() const { - return shading; - } - - const std::string &Culling() const { - return culling; - } - - /** Get material links */ - const std::vector<const Material *> &GetMaterials() const { - return materials; - } - - /** Get geometry links */ - const std::vector<const Geometry *> &GetGeometry() const { - return geometry; - } - - /** Get node attachments */ - const std::vector<const NodeAttribute *> &GetAttributes() const { - return attributes; - } - - /** convenience method to check if the node has a Null node marker */ - bool IsNull() const; - -private: - void ResolveLinks(const ElementPtr element, const Document &doc); - -private: - std::vector<const Material *> materials; - std::vector<const Geometry *> geometry; - std::vector<const NodeAttribute *> attributes; - - std::string shading; - std::string culling; -}; - -class ModelLimbNode : public Model { -public: - ModelLimbNode(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); - virtual ~ModelLimbNode(); -}; - -/** DOM class for generic FBX textures */ -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 { - return type; - } - - const std::string &FileName() const { - return fileName; - } - - const std::string &RelativeFilename() const { - return relativeFileName; - } - - const std::string &AlphaSource() const { - return alphaSource; - } - - const Vector2 &UVTranslation() const { - return uvTrans; - } - - const Vector2 &UVScaling() const { - return uvScaling; - } - - // return a 4-tuple - const unsigned int *Crop() const { - return crop; - } - - const Video *Media() const { - return media; - } - -private: - Vector2 uvTrans; - Vector2 uvScaling; - - std::string type; - std::string relativeFileName; - std::string fileName; - std::string alphaSource; - - unsigned int crop[4] = { 0 }; - const Video *media = nullptr; -}; - -/** DOM class for layered FBX textures */ -class LayeredTexture : public Object { -public: - LayeredTexture(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); - virtual ~LayeredTexture(); - - // Can only be called after construction of the layered texture object due to construction flag. - void fillTexture(const Document &doc); - - enum BlendMode { - BlendMode_Translucent, - BlendMode_Additive, - BlendMode_Modulate, - BlendMode_Modulate2, - BlendMode_Over, - BlendMode_Normal, - BlendMode_Dissolve, - BlendMode_Darken, - BlendMode_ColorBurn, - BlendMode_LinearBurn, - BlendMode_DarkerColor, - BlendMode_Lighten, - BlendMode_Screen, - BlendMode_ColorDodge, - BlendMode_LinearDodge, - BlendMode_LighterColor, - BlendMode_SoftLight, - BlendMode_HardLight, - BlendMode_VividLight, - BlendMode_LinearLight, - BlendMode_PinLight, - BlendMode_HardMix, - BlendMode_Difference, - BlendMode_Exclusion, - BlendMode_Subtract, - BlendMode_Divide, - BlendMode_Hue, - BlendMode_Saturation, - BlendMode_Color, - BlendMode_Luminosity, - BlendMode_Overlay, - BlendMode_BlendModeCount - }; - - const Texture *getTexture(int index = 0) const { - return textures[index]; - } - int textureCount() const { - return static_cast<int>(textures.size()); - } - BlendMode GetBlendMode() const { - return blendMode; - } - float Alpha() { - return alpha; - } - -private: - std::vector<const Texture *> textures; - BlendMode blendMode = BlendMode::BlendMode_Additive; - float alpha = 0; -}; - -typedef std::map<std::string, const Texture *> TextureMap; -typedef std::map<std::string, const LayeredTexture *> LayeredTextureMap; - -/** DOM class for generic FBX videos */ -class Video : public Object { -public: - Video(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); - - virtual ~Video(); - - const std::string &Type() const { - return type; - } - - bool IsEmbedded() const { - return contentLength > 0; - } - - const std::string &FileName() const { - return fileName; - } - - const std::string &RelativeFilename() const { - return relativeFileName; - } - - const uint8_t *Content() const { - return content; - } - - uint64_t ContentLength() const { - return contentLength; - } - - uint8_t *RelinquishContent() { - uint8_t *ptr = content; - content = nullptr; - return ptr; - } - - bool operator==(const Video &other) const { - return ( - type == other.type && relativeFileName == other.relativeFileName && fileName == other.fileName); - } - - bool operator<(const Video &other) const { - return std::tie(type, relativeFileName, fileName) < std::tie(other.type, other.relativeFileName, other.fileName); - } - -private: - std::string type; - std::string relativeFileName; - std::string fileName; - - uint64_t contentLength = 0; - uint8_t *content = nullptr; -}; - -/** DOM class for generic FBX materials */ -class Material : public Object { -public: - Material(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); - - virtual ~Material(); - - const std::string &GetShadingModel() const { - return shading; - } - - bool IsMultilayer() const { - return multilayer; - } - - const TextureMap &Textures() const { - return textures; - } - - const LayeredTextureMap &LayeredTextures() const { - return layeredTextures; - } - -private: - std::string shading; - bool multilayer = false; - - TextureMap textures; - LayeredTextureMap layeredTextures; -}; - -// signed int keys (this can happen!) -typedef std::vector<int64_t> KeyTimeList; -typedef std::vector<float> KeyValueList; - -/** Represents a FBX animation curve (i.e. a 1-dimensional set of keyframes and values therefore) */ -class AnimationCurve : public Object { -public: - AnimationCurve(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc); - virtual ~AnimationCurve(); - - /** get list of keyframe positions (time). - * Invariant: |GetKeys()| > 0 */ - const KeyTimeList &GetKeys() const { - return keys; - } - - /** get list of keyframe values. - * Invariant: |GetKeys()| == |GetValues()| && |GetKeys()| > 0*/ - const KeyValueList &GetValues() const { - return values; - } - - const std::map<int64_t, float> &GetValueTimeTrack() const { - return keyvalues; - } - - const std::vector<float> &GetAttributes() const { - return attributes; - } - - const std::vector<unsigned int> &GetFlags() const { - return flags; - } - -private: - KeyTimeList keys; - KeyValueList values; - std::vector<float> attributes; - std::map<int64_t, float> keyvalues; - std::vector<unsigned int> flags; -}; - -/* Typedef for pointers for the animation handler */ -typedef std::shared_ptr<AnimationCurve> AnimationCurvePtr; -typedef std::weak_ptr<AnimationCurve> AnimationCurveWeakPtr; -typedef std::map<std::string, const AnimationCurve *> AnimationMap; - -/* Animation Curve node ptr */ -typedef std::shared_ptr<AnimationCurveNode> AnimationCurveNodePtr; -typedef std::weak_ptr<AnimationCurveNode> AnimationCurveNodeWeakPtr; - -/** Represents a FBX animation curve (i.e. a mapping from single animation curves to nodes) */ -class AnimationCurveNode : public Object { -public: - /* the optional white list specifies a list of property names for which the caller - wants animations for. If the curve node does not match one of these, std::range_error - will be thrown. */ - AnimationCurveNode(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc, - const char *const *target_prop_whitelist = nullptr, size_t whitelist_size = 0); - - virtual ~AnimationCurveNode(); - - const AnimationMap &Curves() const; - - /** Object the curve is assigned to, this can be nullptr if the - * target object has no DOM representation or could not - * be read for other reasons.*/ - Object *Target() const { - return target; - } - - Model *TargetAsModel() const { - return dynamic_cast<Model *>(target); - } - - NodeAttribute *TargetAsNodeAttribute() const { - return dynamic_cast<NodeAttribute *>(target); - } - - /** Property of Target() that is being animated*/ - const std::string &TargetProperty() const { - return prop; - } - -private: - Object *target = nullptr; - mutable AnimationMap curves; - std::string prop; - const Document &doc; -}; - -typedef std::vector<const AnimationCurveNode *> AnimationCurveNodeList; - -typedef std::shared_ptr<AnimationLayer> AnimationLayerPtr; -typedef std::weak_ptr<AnimationLayer> AnimationLayerWeakPtr; -typedef std::vector<const AnimationLayer *> AnimationLayerList; - -/** Represents a FBX animation layer (i.e. a list of node animations) */ -class AnimationLayer : public Object { -public: - AnimationLayer(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc); - virtual ~AnimationLayer(); - - /* 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 Document &doc; -}; - -/** Represents a FBX animation stack (i.e. a list of animation layers) */ -class AnimationStack : public Object { -public: - AnimationStack(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc); - virtual ~AnimationStack(); - - fbx_simple_property(LocalStart, int64_t, 0L); - fbx_simple_property(LocalStop, int64_t, 0L); - fbx_simple_property(ReferenceStart, int64_t, 0L); - fbx_simple_property(ReferenceStop, int64_t, 0L); - - const AnimationLayerList &Layers() const { - return layers; - } - -private: - AnimationLayerList layers; -}; - -/** DOM class for deformers */ -class Deformer : public Object { -public: - Deformer(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); - virtual ~Deformer(); -}; - -/** Constraints are from Maya they can help us with BoneAttachments :) **/ -class Constraint : public Object { -public: - Constraint(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); - virtual ~Constraint(); -}; - -typedef std::vector<float> WeightArray; -typedef std::vector<unsigned int> WeightIndexArray; - -/** DOM class for BlendShapeChannel deformers */ -class BlendShapeChannel : public Deformer { -public: - BlendShapeChannel(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); - - virtual ~BlendShapeChannel(); - - float DeformPercent() const { - return percent; - } - - const WeightArray &GetFullWeights() const { - return fullWeights; - } - - const std::vector<const ShapeGeometry *> &GetShapeGeometries() const { - return shapeGeometries; - } - -private: - float percent = 0; - WeightArray fullWeights; - std::vector<const ShapeGeometry *> shapeGeometries; -}; - -/** DOM class for BlendShape deformers */ -class BlendShape : public Deformer { -public: - BlendShape(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); - - virtual ~BlendShape(); - - const std::vector<const BlendShapeChannel *> &BlendShapeChannels() const { - return blendShapeChannels; - } - -private: - std::vector<const BlendShapeChannel *> blendShapeChannels; -}; - -/** DOM class for skin deformer clusters (aka sub-deformers) */ -class Cluster : public Deformer { -public: - Cluster(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); - - virtual ~Cluster(); - - /** get the list of deformer weights associated with this cluster. - * Use #GetIndices() to get the associated vertices. Both arrays - * have the same size (and may also be empty). */ - const std::vector<float> &GetWeights() const { - return weights; - } - - /** get indices into the vertex data of the geometry associated - * with this cluster. Use #GetWeights() to get the associated weights. - * Both arrays have the same size (and may also be empty). */ - const std::vector<unsigned int> &GetIndices() const { - return indices; - } - - /** */ - const Transform3D &GetTransform() const { - return transform; - } - - const Transform3D &TransformLink() const { - return transformLink; - } - - const Model *TargetNode() const { - return node; - } - - const Transform3D &TransformAssociateModel() const { - return transformAssociateModel; - } - - bool TransformAssociateModelValid() const { - return valid_transformAssociateModel; - } - - // property is not in the fbx file - // if the cluster has an associate model - // we then have an additive type - enum SkinLinkMode { - SkinLinkMode_Normalized = 0, - SkinLinkMode_Additive = 1 - }; - - SkinLinkMode GetLinkMode() { - return link_mode; - } - -private: - std::vector<float> weights; - std::vector<unsigned int> indices; - - Transform3D transform; - Transform3D transformLink; - Transform3D transformAssociateModel; - SkinLinkMode link_mode; - bool valid_transformAssociateModel = false; - const Model *node = nullptr; -}; - -/** DOM class for skin deformers */ -class Skin : public Deformer { -public: - Skin(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); - - virtual ~Skin(); - - float DeformAccuracy() const { - return accuracy; - } - - const std::vector<const Cluster *> &Clusters() const { - return clusters; - } - - enum SkinType { - Skin_Rigid = 0, - Skin_Linear, - Skin_DualQuaternion, - Skin_Blend - }; - - const SkinType &GetSkinType() const { - return skinType; - } - -private: - float accuracy = 0; - SkinType skinType = SkinType::Skin_Linear; - std::vector<const Cluster *> clusters; -}; - -/** Represents a link between two FBX objects. */ -class Connection { -public: - Connection(uint64_t insertionOrder, uint64_t src, uint64_t dest, const std::string &prop, const Document &doc); - ~Connection(); - - // note: a connection ensures that the source and dest objects exist, but - // not that they have DOM representations, so the return value of one of - // these functions can still be nullptr. - Object *SourceObject() const; - Object *DestinationObject() const; - - // these, however, are always guaranteed to be valid - LazyObject *LazySourceObject() const; - LazyObject *LazyDestinationObject() const; - - /** return the name of the property the connection is attached to. - * this is an empty string for object to object (OO) connections. */ - const std::string &PropertyName() const { - return prop; - } - - uint64_t InsertionOrder() const { - return insertionOrder; - } - - int CompareTo(const Connection *c) const { - //ai_assert(nullptr != c); - - // note: can't subtract because this would overflow uint64_t - if (InsertionOrder() > c->InsertionOrder()) { - return 1; - } else if (InsertionOrder() < c->InsertionOrder()) { - return -1; - } - return 0; - } - - bool Compare(const Connection *c) const { - //ai_assert(nullptr != c); - - return InsertionOrder() < c->InsertionOrder(); - } - -public: - uint64_t insertionOrder = 0; - const std::string prop; - - uint64_t src = 0, dest = 0; - const Document &doc; -}; - -// XXX again, unique_ptr would be useful. shared_ptr is too -// bloated since the objects have a well-defined single owner -// during their entire lifetime (Document). FBX files have -// up to many thousands of objects (most of which we never use), -// so the memory overhead for them should be kept at a minimum. -typedef std::map<uint64_t, LazyObject *> ObjectMap; -typedef std::map<std::string, const PropertyTable *> PropertyTemplateMap; -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 : public PropertyTable { -public: - FileGlobalSettings(const Document &doc); - virtual ~FileGlobalSettings(); - - const Document &GetDocument() const { - return doc; - } - - fbx_simple_property(UpAxis, int, 1); - fbx_simple_property(UpAxisSign, int, 1); - fbx_simple_property(FrontAxis, int, 2); - fbx_simple_property(FrontAxisSign, int, 1); - fbx_simple_property(CoordAxis, int, 0); - fbx_simple_property(CoordAxisSign, int, 1); - fbx_simple_property(OriginalUpAxis, int, 0); - fbx_simple_property(OriginalUpAxisSign, int, 1); - fbx_simple_property(UnitScaleFactor, float, 1); - fbx_simple_property(OriginalUnitScaleFactor, float, 1); - fbx_simple_property(AmbientColor, Vector3, Vector3(0, 0, 0)); - fbx_simple_property(DefaultCamera, std::string, ""); - - enum FrameRate { - FrameRate_DEFAULT = 0, - FrameRate_120 = 1, - FrameRate_100 = 2, - FrameRate_60 = 3, - FrameRate_50 = 4, - FrameRate_48 = 5, - FrameRate_30 = 6, - FrameRate_30_DROP = 7, - FrameRate_NTSC_DROP_FRAME = 8, - FrameRate_NTSC_FULL_FRAME = 9, - FrameRate_PAL = 10, - FrameRate_CINEMA = 11, - FrameRate_1000 = 12, - FrameRate_CINEMA_ND = 13, - FrameRate_CUSTOM = 14, - - FrameRate_MAX // end-of-enum sentinel - }; - - fbx_simple_enum_property(TimeMode, FrameRate, FrameRate_DEFAULT); - fbx_simple_property(TimeSpanStart, uint64_t, 0L); - fbx_simple_property(TimeSpanStop, uint64_t, 0L); - fbx_simple_property(CustomFrameRate, float, -1.0f); - -private: - const Document &doc; -}; - -/** DOM root for a FBX file */ -class Document { -public: - Document(const Parser &parser, const ImportSettings &settings); - - ~Document(); - - LazyObject *GetObject(uint64_t id) const; - - bool IsSafeToImport() const { - return SafeToImport; - } - - bool IsBinary() const { - return parser.IsBinary(); - } - - unsigned int FBXVersion() const { - return fbxVersion; - } - - const std::string &Creator() const { - return creator; - } - - // elements (in this order): Year, Month, Day, Hour, Second, Millisecond - const unsigned int *CreationTimeStamp() const { - return creationTimeStamp; - } - - const FileGlobalSettings *GlobalSettingsPtr() const { - return globals.get(); - } - - const PropertyTable &GetMetadataProperties() const { - return metadata_properties; - } - - const PropertyTemplateMap &Templates() const { - return templates; - } - - const ObjectMap &Objects() const { - return objects; - } - - const ImportSettings &Settings() const { - return settings; - } - - const ConnectionMap &ConnectionsBySource() const { - return src_connections; - } - - const ConnectionMap &ConnectionsByDestination() const { - return dest_connections; - } - - // note: the implicit rule in all DOM classes is to always resolve - // from destination to source (since the FBX object hierarchy is, - // with very few exceptions, a DAG, this avoids cycles). In all - // cases that may involve back-facing edges in the object graph, - // use LazyObject::IsBeingConstructed() to check. - - std::vector<const Connection *> GetConnectionsBySourceSequenced(uint64_t source) const; - std::vector<const Connection *> GetConnectionsByDestinationSequenced(uint64_t dest) const; - - std::vector<const Connection *> GetConnectionsBySourceSequenced(uint64_t source, const char *classname) const; - std::vector<const Connection *> GetConnectionsByDestinationSequenced(uint64_t dest, const char *classname) const; - - std::vector<const Connection *> GetConnectionsBySourceSequenced(uint64_t source, - const char *const *classnames, size_t count) const; - std::vector<const Connection *> GetConnectionsByDestinationSequenced(uint64_t dest, - const char *const *classnames, - size_t count) const; - - const std::vector<const AnimationStack *> &AnimationStacks() const; - const std::vector<uint64_t> &GetAnimationStackIDs() const { - return animationStacks; - } - - const std::vector<uint64_t> &GetConstraintStackIDs() const { - return constraints; - } - - const std::vector<uint64_t> &GetBindPoseIDs() const { - return bind_poses; - }; - - const std::vector<uint64_t> &GetMaterialIDs() const { - return materials; - }; - - const std::vector<uint64_t> &GetSkinIDs() const { - return skins; - } - -private: - std::vector<const Connection *> GetConnectionsSequenced(uint64_t id, const ConnectionMap &) const; - std::vector<const Connection *> GetConnectionsSequenced(uint64_t id, bool is_src, - const ConnectionMap &, - const char *const *classnames, - size_t count) const; - bool ReadHeader(); - void ReadObjects(); - void ReadPropertyTemplates(); - void ReadConnections(); - void ReadGlobalSettings(); - -private: - const ImportSettings &settings; - - ObjectMap objects; - const Parser &parser; - bool SafeToImport = false; - - PropertyTemplateMap templates; - ConnectionMap src_connections; - ConnectionMap dest_connections; - - unsigned int fbxVersion = 0; - std::string creator; - unsigned int creationTimeStamp[7] = { 0 }; - - std::vector<uint64_t> animationStacks; - std::vector<uint64_t> bind_poses; - // constraints aren't in the tree / at least they are not easy to access. - std::vector<uint64_t> constraints; - std::vector<uint64_t> materials; - std::vector<uint64_t> skins; - mutable std::vector<const AnimationStack *> animationStacksResolved; - PropertyTable metadata_properties; - std::shared_ptr<FileGlobalSettings> globals = nullptr; -}; -} // namespace FBXDocParser - -namespace std { -template <> -struct hash<const FBXDocParser::Video> { - std::size_t operator()(const FBXDocParser::Video &video) const { - using std::hash; - using std::size_t; - using std::string; - - size_t res = 17; - res = res * 31 + hash<string>()(video.Name()); - res = res * 31 + hash<string>()(video.RelativeFilename()); - res = res * 31 + hash<string>()(video.Type()); - - return res; - } -}; -} // namespace std - -#endif // FBX_DOCUMENT_H diff --git a/modules/fbx/fbx_parser/FBXDocumentUtil.cpp b/modules/fbx/fbx_parser/FBXDocumentUtil.cpp deleted file mode 100644 index 4a33024969..0000000000 --- a/modules/fbx/fbx_parser/FBXDocumentUtil.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/*************************************************************************/ -/* FBXDocumentUtil.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXDocumentUtil.cpp - * @brief Implementation of the FBX DOM utility functions declared in FBXDocumentUtil.h - */ - -#include "FBXDocumentUtil.h" -#include "FBXDocument.h" -#include "FBXParser.h" -#include "FBXProperties.h" -#include "FBXUtil.h" -#include "core/string/print_string.h" - -namespace FBXDocParser { -namespace Util { - -void DOMError(const std::string &message) { - print_error("[FBX-DOM]" + String(message.c_str())); -} - -void DOMError(const std::string &message, const Token *token) { - print_error("[FBX-DOM]" + String(message.c_str()) + ";" + String(token->StringContents().c_str())); -} - -void DOMError(const std::string &message, const std::shared_ptr<Token> token) { - print_error("[FBX-DOM]" + String(message.c_str()) + ";" + String(token->StringContents().c_str())); -} - -void DOMError(const std::string &message, const Element *element /*= nullptr*/) { - if (element) { - DOMError(message, element->KeyToken()); - } - print_error("[FBX-DOM] " + String(message.c_str())); -} - -void DOMError(const std::string &message, const std::shared_ptr<Element> element /*= nullptr*/) { - if (element) { - DOMError(message, element->KeyToken()); - } - print_error("[FBX-DOM] " + String(message.c_str())); -} - -void DOMWarning(const std::string &message) { - print_verbose("[FBX-DOM] warning:" + String(message.c_str())); -} - -void DOMWarning(const std::string &message, const Token *token) { - print_verbose("[FBX-DOM] warning:" + String(message.c_str()) + ";" + String(token->StringContents().c_str())); -} - -void DOMWarning(const std::string &message, const Element *element /*= nullptr*/) { - if (element) { - DOMWarning(message, element->KeyToken()); - return; - } - print_verbose("[FBX-DOM] warning:" + String(message.c_str())); -} - -void DOMWarning(const std::string &message, const std::shared_ptr<Token> token) { - print_verbose("[FBX-DOM] warning:" + String(message.c_str()) + ";" + String(token->StringContents().c_str())); -} - -void DOMWarning(const std::string &message, const std::shared_ptr<Element> element /*= nullptr*/) { - if (element) { - DOMWarning(message, element->KeyToken()); - return; - } - print_verbose("[FBX-DOM] warning:" + String(message.c_str())); -} - -} // namespace Util -} // namespace FBXDocParser diff --git a/modules/fbx/fbx_parser/FBXDocumentUtil.h b/modules/fbx/fbx_parser/FBXDocumentUtil.h deleted file mode 100644 index 0489ce10ce..0000000000 --- a/modules/fbx/fbx_parser/FBXDocumentUtil.h +++ /dev/null @@ -1,134 +0,0 @@ -/*************************************************************************/ -/* FBXDocumentUtil.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2012, assimp team -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXDocumentUtil.h - * @brief FBX internal utilities used by the DOM reading code - */ -#ifndef FBX_DOCUMENT_UTIL_H -#define FBX_DOCUMENT_UTIL_H - -#include "FBXDocument.h" -#include <memory> -#include <string> - -struct Token; -struct Element; - -namespace FBXDocParser { -namespace Util { - -// Parser errors -void DOMError(const std::string &message); -void DOMError(const std::string &message, const Token *token); -void DOMError(const std::string &message, const Element *element); -void DOMError(const std::string &message, const std::shared_ptr<Element> element); -void DOMError(const std::string &message, const std::shared_ptr<Token> token); - -// Parser warnings -void DOMWarning(const std::string &message); -void DOMWarning(const std::string &message, const Token *token); -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); - -// ------------------------------------------------------------------------------------------------ -template <typename T> -const T *ProcessSimpleConnection(const Connection &con, - bool is_object_property_conn, - const char *name, - const ElementPtr element, - const char **propNameOut = nullptr) { - if (is_object_property_conn && !con.PropertyName().length()) { - DOMWarning("expected incoming " + std::string(name) + - " link to be an object-object connection, ignoring", - element); - return nullptr; - } else if (!is_object_property_conn && con.PropertyName().length()) { - DOMWarning("expected incoming " + std::string(name) + - " link to be an object-property connection, ignoring", - element); - return nullptr; - } - - if (is_object_property_conn && propNameOut) { - // note: this is ok, the return value of PropertyValue() is guaranteed to - // remain valid and unchanged as long as the document exists. - *propNameOut = con.PropertyName().c_str(); - } - - // Cast Object to AnimationPlayer for example using safe functions, which return nullptr etc - Object *ob = con.SourceObject(); - ERR_FAIL_COND_V_MSG(!ob, nullptr, "Failed to load object from SourceObject ptr"); - return dynamic_cast<const T *>(ob); -} -} // namespace Util -} // namespace FBXDocParser - -#endif // FBX_DOCUMENT_UTIL_H diff --git a/modules/fbx/fbx_parser/FBXImportSettings.h b/modules/fbx/fbx_parser/FBXImportSettings.h deleted file mode 100644 index bc22386957..0000000000 --- a/modules/fbx/fbx_parser/FBXImportSettings.h +++ /dev/null @@ -1,162 +0,0 @@ -/*************************************************************************/ -/* FBXImportSettings.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXImportSettings.h - * @brief FBX importer runtime configuration - */ -#ifndef FBX_IMPORT_SETTINGS_H -#define FBX_IMPORT_SETTINGS_H - -namespace FBXDocParser { - -/** FBX import settings, parts of which are publicly accessible via their corresponding AI_CONFIG constants */ -struct ImportSettings { - /** enable strict mode: - * - only accept fbx 2012, 2013 files - * - on the slightest error, give up. - * - * Basically, strict mode means that the fbx file will actually - * be validated.*/ - bool strictMode = true; - - /** specifies whether all geometry layers are read and scanned for - * usable data channels. The FBX spec indicates that many readers - * will only read the first channel and that this is in some way - * the recommended way- in reality, however, it happens a lot that - * vertex data is spread among multiple layers.*/ - bool readAllLayers = true; - - /** specifies whether all materials are read, or only those that - * are referenced by at least one mesh. Reading all materials - * may make FBX reading a lot slower since all objects - * need to be processed. - * This bit is ignored unless readMaterials=true.*/ - bool readAllMaterials = true; - - /** import materials (true) or skip them and assign a default - * material.*/ - bool readMaterials = true; - - /** import embedded textures?*/ - bool readTextures = true; - - /** import cameras?*/ - bool readCameras = true; - - /** import light sources?*/ - bool readLights = true; - - /** import animations (i.e. animation curves, the node - * skeleton is always imported).*/ - bool readAnimations = true; - - /** read bones (vertex weights and deform info).*/ - bool readWeights = true; - - /** preserve transformation pivots and offsets. Since these can - * not directly be represented in assimp, additional dummy - * nodes will be generated. Note that settings this to false - * can make animation import a lot slower. - * - * The naming scheme for the generated nodes is: - * <OriginalName>_$AssimpFbx$_<TransformName> - * - * where <TransformName> is one of - * RotationPivot - * RotationOffset - * PreRotation - * PostRotation - * ScalingPivot - * ScalingOffset - * Translation - * Scaling - * Rotation - **/ - bool preservePivots = true; - - /** do not import animation curves that specify a constant - * values matching the corresponding node transformation.*/ - bool optimizeEmptyAnimationCurves = true; - - /** use legacy naming for embedded textures eg: (*0, *1, *2).*/ - bool useLegacyEmbeddedTextureNaming = false; - - /** Empty bones shall be removed.*/ - bool removeEmptyBones = true; - - /** Set to true to perform a conversion from cm to meter after - * the import.*/ - bool convertToMeters = false; -}; -} // namespace FBXDocParser - -#endif // FBX_IMPORT_SETTINGS_H diff --git a/modules/fbx/fbx_parser/FBXMaterial.cpp b/modules/fbx/fbx_parser/FBXMaterial.cpp deleted file mode 100644 index bf8922267e..0000000000 --- a/modules/fbx/fbx_parser/FBXMaterial.cpp +++ /dev/null @@ -1,388 +0,0 @@ -/*************************************************************************/ -/* FBXMaterial.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2020, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXMaterial.cpp - * @brief Assimp::FBX::Material and Assimp::FBX::Texture implementation - */ - -#include "ByteSwapper.h" -#include "FBXDocument.h" -#include "FBXDocumentUtil.h" -#include "FBXImportSettings.h" -#include "FBXParser.h" -#include "FBXProperties.h" - -#include "FBXUtil.h" -#include <algorithm> // std::transform - -namespace FBXDocParser { - -using namespace Util; - -// ------------------------------------------------------------------------------------------------ -Material::Material(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : - Object(id, element, name) { - const ScopePtr sc = GetRequiredScope(element); - - const ElementPtr ShadingModel = sc->GetElement("ShadingModel"); - const ElementPtr MultiLayer = sc->GetElement("MultiLayer"); - - if (MultiLayer) { - multilayer = !!ParseTokenAsInt(GetRequiredToken(MultiLayer, 0)); - } - - if (ShadingModel) { - shading = ParseTokenAsString(GetRequiredToken(ShadingModel, 0)); - } else { - DOMWarning("shading mode not specified, assuming phong", element); - shading = "phong"; - } - - std::string templateName; - - if (shading == "phong") { - templateName = "Material.Phong"; - } else if (shading == "lambert") { - templateName = "Material.Lambert"; - } else if (shading == "unknown") { - templateName = "Material.StingRay"; - } else { - DOMWarning("shading mode not recognized: " + shading, element); - } - - // resolve texture links - const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID()); - for (const Connection *con : conns) { - // texture link to properties, not objects - if (!con->PropertyName().length()) { - continue; - } - - Object *ob = con->SourceObject(); - if (!ob) { - DOMWarning("failed to read source object for texture link, ignoring", element); - continue; - } - - const Texture *tex = dynamic_cast<const Texture *>(ob); - if (!tex) { - LayeredTexture *layeredTexture = dynamic_cast<LayeredTexture *>(ob); - - if (!layeredTexture) { - DOMWarning("source object for texture link is not a texture or layered texture, ignoring", element); - continue; - } - - const std::string &prop = con->PropertyName(); - if (layeredTextures.find(prop) != layeredTextures.end()) { - DOMWarning("duplicate layered texture link: " + prop, element); - } - - layeredTextures[prop] = layeredTexture; - layeredTexture->fillTexture(doc); - } else { - const std::string &prop = con->PropertyName(); - if (textures.find(prop) != textures.end()) { - DOMWarning("duplicate texture link: " + prop, element); - } - - textures[prop] = tex; - } - } -} - -// ------------------------------------------------------------------------------------------------ -Material::~Material() { -} - -// ------------------------------------------------------------------------------------------------ -Texture::Texture(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : - Object(id, element, name), uvScaling(1.0f, 1.0f) { - const ScopePtr sc = GetRequiredScope(element); - - const ElementPtr Type = sc->GetElement("Type"); - const ElementPtr FileName = sc->GetElement("FileName"); - const ElementPtr RelativeFilename = sc->GetElement("RelativeFilename"); - const ElementPtr ModelUVTranslation = sc->GetElement("ModelUVTranslation"); - const ElementPtr ModelUVScaling = sc->GetElement("ModelUVScaling"); - const ElementPtr Texture_Alpha_Source = sc->GetElement("Texture_Alpha_Source"); - const ElementPtr Cropping = sc->GetElement("Cropping"); - - if (Type) { - type = ParseTokenAsString(GetRequiredToken(Type, 0)); - } - - if (FileName) { - fileName = ParseTokenAsString(GetRequiredToken(FileName, 0)); - } - - if (RelativeFilename) { - relativeFileName = ParseTokenAsString(GetRequiredToken(RelativeFilename, 0)); - } - - if (ModelUVTranslation) { - uvTrans = Vector2(ParseTokenAsFloat(GetRequiredToken(ModelUVTranslation, 0)), - ParseTokenAsFloat(GetRequiredToken(ModelUVTranslation, 1))); - } - - if (ModelUVScaling) { - uvScaling = Vector2(ParseTokenAsFloat(GetRequiredToken(ModelUVScaling, 0)), - ParseTokenAsFloat(GetRequiredToken(ModelUVScaling, 1))); - } - - if (Cropping) { - crop[0] = ParseTokenAsInt(GetRequiredToken(Cropping, 0)); - crop[1] = ParseTokenAsInt(GetRequiredToken(Cropping, 1)); - crop[2] = ParseTokenAsInt(GetRequiredToken(Cropping, 2)); - crop[3] = ParseTokenAsInt(GetRequiredToken(Cropping, 3)); - } else { - // vc8 doesn't support the crop() syntax in initialization lists - // (and vc9 WARNS about the new (i.e. compliant) behaviour). - crop[0] = crop[1] = crop[2] = crop[3] = 0; - } - - if (Texture_Alpha_Source) { - alphaSource = ParseTokenAsString(GetRequiredToken(Texture_Alpha_Source, 0)); - } - - // 3DS Max and FBX SDK use "Scaling" and "Translation" instead of "ModelUVScaling" and "ModelUVTranslation". Use these properties if available. - 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>(this, "Translation", ok); - if (ok) { - uvTrans.x = trans.x; - uvTrans.y = trans.y; - } - - // resolve video links - if (doc.Settings().readTextures) { - const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID()); - for (const Connection *con : conns) { - const Object *const ob = con->SourceObject(); - if (!ob) { - DOMWarning("failed to read source object for texture link, ignoring", element); - continue; - } - - const Video *const video = dynamic_cast<const Video *>(ob); - if (video) { - media = video; - } - } - } -} - -Texture::~Texture() { -} - -LayeredTexture::LayeredTexture(uint64_t id, const ElementPtr element, const Document & /*doc*/, const std::string &name) : - Object(id, element, name), blendMode(BlendMode_Modulate), alpha(1) { - const ScopePtr sc = GetRequiredScope(element); - - ElementPtr BlendModes = sc->GetElement("BlendModes"); - ElementPtr Alphas = sc->GetElement("Alphas"); - - if (BlendModes != nullptr) { - blendMode = (BlendMode)ParseTokenAsInt(GetRequiredToken(BlendModes, 0)); - } - if (Alphas != nullptr) { - alpha = ParseTokenAsFloat(GetRequiredToken(Alphas, 0)); - } -} - -LayeredTexture::~LayeredTexture() { -} - -void LayeredTexture::fillTexture(const Document &doc) { - const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID()); - for (size_t i = 0; i < conns.size(); ++i) { - const Connection *con = conns.at(i); - - const Object *const ob = con->SourceObject(); - if (!ob) { - DOMWarning("failed to read source object for texture link, ignoring", element); - continue; - } - - const Texture *const tex = dynamic_cast<const Texture *>(ob); - - textures.push_back(tex); - } -} - -// ------------------------------------------------------------------------------------------------ -Video::Video(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : - Object(id, element, name) { - const ScopePtr sc = GetRequiredScope(element); - - const ElementPtr Type = sc->GetElement("Type"); - // File Version 7500 Crashes if this is not checked fully. - // As of writing this comment 7700 exists, in August 2020 - ElementPtr FileName = nullptr; - if (HasElement(sc, "Filename")) { - FileName = (ElementPtr)sc->GetElement("Filename"); - } else if (HasElement(sc, "FileName")) { - FileName = (ElementPtr)sc->GetElement("FileName"); - } else { - print_error("file has invalid video material returning..."); - return; - } - const ElementPtr RelativeFilename = sc->GetElement("RelativeFilename"); - const ElementPtr Content = sc->GetElement("Content"); - - if (Type) { - type = ParseTokenAsString(GetRequiredToken(Type, 0)); - } - - if (FileName) { - fileName = ParseTokenAsString(GetRequiredToken(FileName, 0)); - } - - if (RelativeFilename) { - relativeFileName = ParseTokenAsString(GetRequiredToken(RelativeFilename, 0)); - } - - if (Content && !Content->Tokens().empty()) { - //this field is omitted when the embedded texture is already loaded, let's ignore if it's not found - try { - const Token *token = GetRequiredToken(Content, 0); - const char *data = token->begin(); - if (!token->IsBinary()) { - if (*data != '"') { - DOMError("embedded content is not surrounded by quotation marks", element); - } else { - size_t targetLength = 0; - const size_t numTokens = Content->Tokens().size(); - // First time compute size (it could be large like 64Gb and it is good to allocate it once) - for (uint32_t tokenIdx = 0; tokenIdx < numTokens; ++tokenIdx) { - const Token *dataToken = GetRequiredToken(Content, tokenIdx); - size_t tokenLength = dataToken->end() - dataToken->begin() - 2; // ignore double quotes - const char *base64data = dataToken->begin() + 1; - const size_t outLength = Util::ComputeDecodedSizeBase64(base64data, tokenLength); - if (outLength == 0) { - DOMError("Corrupted embedded content found", element); - } - targetLength += outLength; - } - if (targetLength == 0) { - DOMError("Corrupted embedded content found", element); - } else { - content = new uint8_t[targetLength]; - contentLength = static_cast<uint64_t>(targetLength); - size_t dst_offset = 0; - for (uint32_t tokenIdx = 0; tokenIdx < numTokens; ++tokenIdx) { - const Token *dataToken = GetRequiredToken(Content, tokenIdx); - ERR_FAIL_COND(!dataToken); - size_t tokenLength = dataToken->end() - dataToken->begin() - 2; // ignore double quotes - const char *base64data = dataToken->begin() + 1; - dst_offset += Util::DecodeBase64(base64data, tokenLength, content + dst_offset, targetLength - dst_offset); - } - if (targetLength != dst_offset) { - delete[] content; - contentLength = 0; - DOMError("Corrupted embedded content found", element); - } - } - } - } else if (static_cast<size_t>(token->end() - data) < 5) { - DOMError("binary data array is too short, need five (5) bytes for type signature and element count", element); - } else if (*data != 'R') { - DOMWarning("video content is not raw binary data, ignoring", element); - } else { - // read number of elements - uint32_t len = 0; - ::memcpy(&len, data + 1, sizeof(len)); - AI_SWAP4(len); - - contentLength = len; - - content = new uint8_t[len]; - ::memcpy(content, data + 5, len); - } - } catch (...) { - // //we don't need the content data for contents that has already been loaded - // ASSIMP_LOG_VERBOSE_DEBUG_F("Caught exception in FBXMaterial (likely because content was already loaded): ", - // runtimeError.what()); - } - } -} - -Video::~Video() { - if (content) { - delete[] content; - } -} -} // namespace FBXDocParser diff --git a/modules/fbx/fbx_parser/FBXMeshGeometry.cpp b/modules/fbx/fbx_parser/FBXMeshGeometry.cpp deleted file mode 100644 index 2cc25a0690..0000000000 --- a/modules/fbx/fbx_parser/FBXMeshGeometry.cpp +++ /dev/null @@ -1,485 +0,0 @@ -/*************************************************************************/ -/* FBXMeshGeometry.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXMeshGeometry.cpp - * @brief Assimp::FBX::MeshGeometry implementation - */ - -#include <functional> - -#include "FBXDocument.h" -#include "FBXDocumentUtil.h" -#include "FBXImportSettings.h" -#include "FBXMeshGeometry.h" -#include "core/math/vector3.h" - -namespace FBXDocParser { - -using namespace Util; - -// ------------------------------------------------------------------------------------------------ -Geometry::Geometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) : - Object(id, element, name) { - const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Deformer"); - for (const Connection *con : conns) { - const Skin *sk = ProcessSimpleConnection<Skin>(*con, false, "Skin -> Geometry", element); - if (sk) { - skin = sk; - } - const BlendShape *bsp = ProcessSimpleConnection<BlendShape>(*con, false, "BlendShape -> Geometry", - element); - if (bsp) { - blendShapes.push_back(bsp); - } - } -} - -// ------------------------------------------------------------------------------------------------ -Geometry::~Geometry() { - // empty -} - -// ------------------------------------------------------------------------------------------------ -const std::vector<const BlendShape *> &Geometry::get_blend_shapes() const { - return blendShapes; -} - -// ------------------------------------------------------------------------------------------------ -const Skin *Geometry::DeformerSkin() const { - return skin; -} - -// ------------------------------------------------------------------------------------------------ -MeshGeometry::MeshGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) : - Geometry(id, element, name, doc) { - print_verbose("mesh name: " + String(name.c_str())); - - ScopePtr sc = element->Compound(); - ERR_FAIL_COND_MSG(sc == nullptr, "failed to read geometry, prevented crash"); - ERR_FAIL_COND_MSG(!HasElement(sc, "Vertices"), "Detected mesh with no vertices, didn't populate the mesh"); - - // must have Mesh elements: - const ElementPtr Vertices = GetRequiredElement(sc, "Vertices", element); - const ElementPtr PolygonVertexIndex = GetRequiredElement(sc, "PolygonVertexIndex", element); - - if (HasElement(sc, "Edges")) { - const ElementPtr element_edges = GetRequiredElement(sc, "Edges", element); - ParseVectorDataArray(m_edges, element_edges); - } - - // read mesh data into arrays - ParseVectorDataArray(m_vertices, Vertices); - ParseVectorDataArray(m_face_indices, PolygonVertexIndex); - - ERR_FAIL_COND_MSG(m_vertices.empty(), "mesh with no vertices in FBX file, did you mean to delete it?"); - ERR_FAIL_COND_MSG(m_face_indices.empty(), "mesh has no faces, was this intended?"); - - // Retrieve layer elements, for all of the mesh - const ElementCollection &Layer = sc->GetCollection("Layer"); - - // Store all layers - std::vector<std::tuple<int, std::string>> valid_layers; - - // now read the sub mesh information from the geometry (normals, uvs, etc) - for (ElementMap::const_iterator it = Layer.first; it != Layer.second; ++it) { - const ScopePtr layer = GetRequiredScope(it->second); - const ElementCollection &LayerElement = layer->GetCollection("LayerElement"); - for (ElementMap::const_iterator eit = LayerElement.first; eit != LayerElement.second; ++eit) { - std::string layer_name = eit->first; - ElementPtr element_layer = eit->second; - const ScopePtr layer_element = GetRequiredScope(element_layer); - - // Actual usable 'type' LayerElementUV, LayerElementNormal, etc - const ElementPtr Type = GetRequiredElement(layer_element, "Type"); - const ElementPtr TypedIndex = GetRequiredElement(layer_element, "TypedIndex"); - const std::string &type = ParseTokenAsString(GetRequiredToken(Type, 0)); - const int typedIndex = ParseTokenAsInt(GetRequiredToken(TypedIndex, 0)); - - // we only need the layer name and the typed index. - valid_layers.push_back(std::tuple<int, std::string>(typedIndex, type)); - } - } - - // get object / mesh directly from the FBX by the element ID. - const ScopePtr top = GetRequiredScope(element); - - // iterate over all layers for the mesh (uvs, normals, smoothing groups, colors, etc) - for (size_t x = 0; x < valid_layers.size(); x++) { - const int layer_id = std::get<0>(valid_layers[x]); - const std::string &layer_type_name = std::get<1>(valid_layers[x]); - - // Get collection of elements from the XLayerMap (example: LayerElementUV) - // this must contain our proper elements. - - // This is stupid, because it means we select them ALL not just the one we want. - // but it's fine we can match by id. - - const ElementCollection &candidates = top->GetCollection(layer_type_name); - - ElementMap::const_iterator iter; - for (iter = candidates.first; iter != candidates.second; ++iter) { - const ScopePtr layer_scope = GetRequiredScope(iter->second); - TokenPtr layer_token = GetRequiredToken(iter->second, 0); - const int index = ParseTokenAsInt(layer_token); - - ERR_FAIL_COND_MSG(layer_scope == nullptr, "prevented crash, layer scope is invalid"); - - if (index == layer_id) { - const std::string &MappingInformationType = ParseTokenAsString(GetRequiredToken( - GetRequiredElement(layer_scope, "MappingInformationType"), 0)); - - const std::string &ReferenceInformationType = ParseTokenAsString(GetRequiredToken( - GetRequiredElement(layer_scope, "ReferenceInformationType"), 0)); - - if (layer_type_name == "LayerElementUV") { - if (index == 0) { - m_uv_0 = resolve_vertex_data_array<Vector2>(layer_scope, MappingInformationType, ReferenceInformationType, "UV"); - } else if (index == 1) { - m_uv_1 = resolve_vertex_data_array<Vector2>(layer_scope, MappingInformationType, ReferenceInformationType, "UV"); - } - } else if (layer_type_name == "LayerElementMaterial") { - m_material_allocation_ids = resolve_vertex_data_array<int>(layer_scope, MappingInformationType, ReferenceInformationType, "Materials"); - } else if (layer_type_name == "LayerElementNormal") { - m_normals = resolve_vertex_data_array<Vector3>(layer_scope, MappingInformationType, ReferenceInformationType, "Normals"); - } else if (layer_type_name == "LayerElementColor") { - m_colors = resolve_vertex_data_array<Color>(layer_scope, MappingInformationType, ReferenceInformationType, "Colors", "ColorIndex"); - // NOTE: this is a useful sanity check to ensure you're getting any color data which is not default. - // const Color first_color_check = m_colors.data[0]; - // bool colors_are_all_the_same = true; - // size_t i = 1; - // for(i = 1; i < m_colors.data.size(); i++) - // { - // const Color current_color = m_colors.data[i]; - // if(current_color.is_equal_approx(first_color_check)) - // { - // continue; - // } - // else - // { - // colors_are_all_the_same = false; - // break; - // } - // } - // - // if(colors_are_all_the_same) - // { - // print_error("Color serialisation is not working for vertex colors some should be different in the test asset."); - // } - // else - // { - // print_verbose("Color array has unique colors at index: " + itos(i)); - // } - } - } - } - } - - print_verbose("Mesh statistics \nuv_0: " + m_uv_0.debug_info() + "\nuv_1: " + m_uv_1.debug_info() + "\nvertices: " + itos(m_vertices.size())); - - // Compose the edge of the mesh. - // You can see how the edges are stored into the FBX here: https://gist.github.com/AndreaCatania/da81840f5aa3b2feedf189e26c5a87e6 - for (size_t i = 0; i < m_edges.size(); i += 1) { - ERR_FAIL_INDEX_MSG((size_t)m_edges[i], m_face_indices.size(), "The edge is pointing to a weird location in the face indices. The FBX is corrupted."); - int polygon_vertex_0 = m_face_indices[m_edges[i]]; - int polygon_vertex_1; - if (polygon_vertex_0 < 0) { - // The polygon_vertex_0 points to the end of a polygon, so it's - // connected with the beginning of polygon in the edge list. - - // Fist invert the vertex. - polygon_vertex_0 = ~polygon_vertex_0; - - // Search the start vertex of the polygon. - // Iterate from the polygon_vertex_index backward till the start of - // the polygon is found. - ERR_FAIL_COND_MSG(m_edges[i] - 1 < 0, "The polygon is not yet started and we already need the final vertex. This FBX is corrupted."); - bool found_it = false; - for (int x = m_edges[i] - 1; x >= 0; x -= 1) { - if (x == 0) { - // This for sure is the start. - polygon_vertex_1 = m_face_indices[x]; - found_it = true; - break; - } else if (m_face_indices[x] < 0) { - // This is the end of the previous polygon, so the next is - // the start of the polygon we need. - polygon_vertex_1 = m_face_indices[x + 1]; - found_it = true; - break; - } - } - // As the algorithm above, this check is useless. Because the first - // ever vertex is always considered the beginning of a polygon. - ERR_FAIL_COND_MSG(found_it == false, "Was not possible to find the first vertex of this polygon. FBX file is corrupted."); - - } else { - ERR_FAIL_INDEX_MSG((size_t)(m_edges[i] + 1), m_face_indices.size(), "FBX The other FBX edge seems to point to an invalid vertices. This FBX file is corrupted."); - // Take the next vertex - polygon_vertex_1 = m_face_indices[m_edges[i] + 1]; - } - - if (polygon_vertex_1 < 0) { - // We don't care if the `polygon_vertex_1` is the end of the polygon, - // for `polygon_vertex_1` so we can just invert it. - polygon_vertex_1 = ~polygon_vertex_1; - } - - ERR_FAIL_COND_MSG(polygon_vertex_0 == polygon_vertex_1, "The vertices of this edge can't be the same, Is this a point???. This FBX file is corrupted."); - - // Just create the edge. - edge_map.push_back({ polygon_vertex_0, polygon_vertex_1 }); - } -} - -MeshGeometry::~MeshGeometry() { - // empty -} - -const std::vector<Vector3> &MeshGeometry::get_vertices() const { - return m_vertices; -} - -const std::vector<MeshGeometry::Edge> &MeshGeometry::get_edge_map() const { - return edge_map; -} - -const std::vector<int> &MeshGeometry::get_polygon_indices() const { - return m_face_indices; -} - -const std::vector<int> &MeshGeometry::get_edges() const { - return m_edges; -} - -const MeshGeometry::MappingData<Vector3> &MeshGeometry::get_normals() const { - return m_normals; -} - -const MeshGeometry::MappingData<Vector2> &MeshGeometry::get_uv_0() const { - //print_verbose("get uv_0 " + m_uv_0.debug_info() ); - return m_uv_0; -} - -const MeshGeometry::MappingData<Vector2> &MeshGeometry::get_uv_1() const { - //print_verbose("get uv_1 " + m_uv_1.debug_info() ); - return m_uv_1; -} - -const MeshGeometry::MappingData<Color> &MeshGeometry::get_colors() const { - return m_colors; -} - -const MeshGeometry::MappingData<int> &MeshGeometry::get_material_allocation_id() const { - return m_material_allocation_ids; -} - -int MeshGeometry::get_edge_id(const std::vector<Edge> &p_map, int p_vertex_a, int p_vertex_b) { - for (size_t i = 0; i < p_map.size(); i += 1) { - if ((p_map[i].vertex_0 == p_vertex_a && p_map[i].vertex_1 == p_vertex_b) || (p_map[i].vertex_1 == p_vertex_a && p_map[i].vertex_0 == p_vertex_b)) { - return i; - } - } - return -1; -} - -MeshGeometry::Edge MeshGeometry::get_edge(const std::vector<Edge> &p_map, int p_id) { - ERR_FAIL_INDEX_V_MSG((size_t)p_id, p_map.size(), Edge({ -1, -1 }), "ID not found."); - return p_map[p_id]; -} - -template <class T> -MeshGeometry::MappingData<T> MeshGeometry::resolve_vertex_data_array( - const ScopePtr source, - const std::string &MappingInformationType, - const std::string &ReferenceInformationType, - const std::string &dataElementName, - const std::string &indexOverride) { - ERR_FAIL_COND_V_MSG(source == nullptr, MappingData<T>(), "Invalid scope operator preventing memory corruption"); - - // UVIndex, MaterialIndex, NormalIndex, etc.. - std::string indexDataElementName; - - if (indexOverride != "") { - // Colors should become ColorIndex - indexDataElementName = indexOverride; - } else { - // Some indexes will exist. - indexDataElementName = dataElementName + "Index"; - } - - // goal: expand everything to be per vertex - - ReferenceType l_ref_type = ReferenceType::direct; - - // Read the reference type into the enumeration - if (ReferenceInformationType == "IndexToDirect") { - l_ref_type = ReferenceType::index_to_direct; - } else if (ReferenceInformationType == "Index") { - // set non legacy index to direct mapping - l_ref_type = ReferenceType::index; - } else if (ReferenceInformationType == "Direct") { - l_ref_type = ReferenceType::direct; - } else { - ERR_FAIL_V_MSG(MappingData<T>(), "invalid reference type has the FBX format changed?"); - } - - MapType l_map_type = MapType::none; - - if (MappingInformationType == "None") { - l_map_type = MapType::none; - } else if (MappingInformationType == "ByVertice") { - l_map_type = MapType::vertex; - } else if (MappingInformationType == "ByPolygonVertex") { - l_map_type = MapType::polygon_vertex; - } else if (MappingInformationType == "ByPolygon") { - l_map_type = MapType::polygon; - } else if (MappingInformationType == "ByEdge") { - l_map_type = MapType::edge; - } else if (MappingInformationType == "AllSame") { - l_map_type = MapType::all_the_same; - } else { - print_error("invalid mapping type: " + String(MappingInformationType.c_str())); - } - - // create mapping data - MeshGeometry::MappingData<T> tempData; - tempData.map_type = l_map_type; - tempData.ref_type = l_ref_type; - - // parse data into array - ParseVectorDataArray(tempData.data, GetRequiredElement(source, dataElementName)); - - // index array won't always exist - const ElementPtr element = GetOptionalElement(source, indexDataElementName); - if (element) { - ParseVectorDataArray(tempData.index, element); - } - - return tempData; -} -// ------------------------------------------------------------------------------------------------ -ShapeGeometry::ShapeGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) : - Geometry(id, element, name, doc) { - const ScopePtr sc = element->Compound(); - if (nullptr == sc) { - DOMError("failed to read Geometry object (class: Shape), no data scope found"); - } - const ElementPtr Indexes = GetRequiredElement(sc, "Indexes", element); - const ElementPtr Normals = GetRequiredElement(sc, "Normals", element); - const ElementPtr Vertices = GetRequiredElement(sc, "Vertices", element); - ParseVectorDataArray(m_indices, Indexes); - ParseVectorDataArray(m_vertices, Vertices); - ParseVectorDataArray(m_normals, Normals); -} - -// ------------------------------------------------------------------------------------------------ -ShapeGeometry::~ShapeGeometry() { - // empty -} -// ------------------------------------------------------------------------------------------------ -const std::vector<Vector3> &ShapeGeometry::GetVertices() const { - return m_vertices; -} -// ------------------------------------------------------------------------------------------------ -const std::vector<Vector3> &ShapeGeometry::GetNormals() const { - return m_normals; -} -// ------------------------------------------------------------------------------------------------ -const std::vector<unsigned int> &ShapeGeometry::GetIndices() const { - return m_indices; -} -// ------------------------------------------------------------------------------------------------ -LineGeometry::LineGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) : - Geometry(id, element, name, doc) { - const ScopePtr sc = element->Compound(); - if (!sc) { - DOMError("failed to read Geometry object (class: Line), no data scope found"); - } - const ElementPtr Points = GetRequiredElement(sc, "Points", element); - const ElementPtr PointsIndex = GetRequiredElement(sc, "PointsIndex", element); - ParseVectorDataArray(m_vertices, Points); - ParseVectorDataArray(m_indices, PointsIndex); -} - -// ------------------------------------------------------------------------------------------------ -LineGeometry::~LineGeometry() { - // empty -} -// ------------------------------------------------------------------------------------------------ -const std::vector<Vector3> &LineGeometry::GetVertices() const { - return m_vertices; -} -// ------------------------------------------------------------------------------------------------ -const std::vector<int> &LineGeometry::GetIndices() const { - return m_indices; -} -} // namespace FBXDocParser diff --git a/modules/fbx/fbx_parser/FBXMeshGeometry.h b/modules/fbx/fbx_parser/FBXMeshGeometry.h deleted file mode 100644 index 26fc1914d1..0000000000 --- a/modules/fbx/fbx_parser/FBXMeshGeometry.h +++ /dev/null @@ -1,263 +0,0 @@ -/*************************************************************************/ -/* FBXMeshGeometry.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -#ifndef FBX_MESH_GEOMETRY_H -#define FBX_MESH_GEOMETRY_H - -#include "core/math/color.h" -#include "core/math/vector2.h" -#include "core/math/vector3.h" -#include "core/templates/vector.h" - -#include "FBXDocument.h" -#include "FBXParser.h" - -#include <iostream> - -#define AI_MAX_NUMBER_OF_TEXTURECOORDS 4 -#define AI_MAX_NUMBER_OF_COLOR_SETS 8 - -namespace FBXDocParser { - -/* - * DOM base class for all kinds of FBX geometry - */ -class Geometry : public Object { -public: - Geometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc); - virtual ~Geometry(); - - /** Get the Skin attached to this geometry or nullptr */ - const Skin *DeformerSkin() const; - - const std::vector<const BlendShape *> &get_blend_shapes() const; - - size_t get_blend_shape_count() const { - return blendShapes.size(); - } - -private: - const Skin *skin = nullptr; - std::vector<const BlendShape *> blendShapes; -}; - -typedef std::vector<int> MatIndexArray; - -/// Map Geometry stores the FBX file information. -/// -/// # FBX doc. -/// ## Reference type declared: -/// - Direct (directly related to the mapping information type) -/// - IndexToDirect (Map with key value, meaning depends on the MappingInformationType) -/// -/// ## Map Type: -/// * None The mapping is undetermined. -/// * ByVertex There will be one mapping coordinate for each surface control point/vertex (ControlPoint is a vertex). -/// * If you have direct reference type vertices[x] -/// * If you have IndexToDirect reference type the UV -/// * ByPolygonVertex There will be one mapping coordinate for each vertex, for every polygon of which it is a part. This means that a vertex will have as many mapping coordinates as polygons of which it is a part. (Sorted by polygon, referencing vertex) -/// * ByPolygon There can be only one mapping coordinate for the whole polygon. -/// * One mapping per polygon polygon x has this normal x -/// * For each vertex of the polygon then set the normal to x -/// * ByEdge There will be one mapping coordinate for each unique edge in the mesh. This is meant to be used with smoothing layer elements. (Mapping is referencing the edge id) -/// * AllSame There can be only one mapping coordinate for the whole surface. -class MeshGeometry : public Geometry { -public: - enum class MapType { - none = 0, // No mapping type. Stored as "None". - vertex, // Maps per vertex. Stored as "ByVertice". - polygon_vertex, // Maps per polygon vertex. Stored as "ByPolygonVertex". - polygon, // Maps per polygon. Stored as "ByPolygon". - edge, // Maps per edge. Stored as "ByEdge". - all_the_same // Uaps to everything. Stored as "AllSame". - }; - - enum class ReferenceType { - direct = 0, - index = 1, - index_to_direct = 2 - }; - - template <class T> - struct MappingData { - MapType map_type = MapType::none; - ReferenceType ref_type = ReferenceType::direct; - std::vector<T> data; - /// The meaning of the indices depends from the `MapType`. - /// If `ref_type` is `direct` this map is hollow. - std::vector<int> index; - - String debug_info() const { - return "indexes: " + itos(index.size()) + " data: " + itos(data.size()); - } - }; - - struct Edge { - int vertex_0 = 0, vertex_1 = 0; - Edge(int v0, int v1) : - vertex_0(v0), vertex_1(v1) {} - Edge() {} - }; - -public: - MeshGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc); - - virtual ~MeshGeometry(); - - const std::vector<Vector3> &get_vertices() const; - const std::vector<Edge> &get_edge_map() const; - const std::vector<int> &get_polygon_indices() const; - const std::vector<int> &get_edges() const; - const MappingData<Vector3> &get_normals() const; - const MappingData<Vector2> &get_uv_0() const; - const MappingData<Vector2> &get_uv_1() const; - const MappingData<Color> &get_colors() const; - const MappingData<int> &get_material_allocation_id() const; - - /// Returns -1 if the vertices doesn't form an edge. Vertex order, doesn't - // matter. - static int get_edge_id(const std::vector<Edge> &p_map, int p_vertex_a, int p_vertex_b); - // Returns the edge point bu that ID, or the edge with -1 vertices if the - // id is not valid. - static Edge get_edge(const std::vector<Edge> &p_map, int p_id); - -private: - // Read directly from the FBX file. - std::vector<Vector3> m_vertices; - std::vector<Edge> edge_map; - std::vector<int> m_face_indices; - std::vector<int> m_edges; - MappingData<Vector3> m_normals; - MappingData<Vector2> m_uv_0; // first uv coordinates - MappingData<Vector2> m_uv_1; // second uv coordinates - MappingData<Color> m_colors; // colors for the mesh - MappingData<int> m_material_allocation_ids; // slot of material used - - template <class T> - MappingData<T> resolve_vertex_data_array( - const ScopePtr source, - const std::string &MappingInformationType, - const std::string &ReferenceInformationType, - const std::string &dataElementName, - const std::string &indexOverride = ""); -}; - -/* - * DOM class for FBX geometry of type "Shape" - */ -class ShapeGeometry : public Geometry { -public: - /** The class constructor */ - ShapeGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc); - - /** The class destructor */ - virtual ~ShapeGeometry(); - - /** Get a list of all vertex points, non-unique*/ - const std::vector<Vector3> &GetVertices() const; - - /** Get a list of all vertex normals or an empty array if - * no normals are specified. */ - const std::vector<Vector3> &GetNormals() const; - - /** Return list of vertex indices. */ - const std::vector<unsigned int> &GetIndices() const; - -private: - std::vector<Vector3> m_vertices; - std::vector<Vector3> m_normals; - std::vector<unsigned int> m_indices; -}; -/** - * DOM class for FBX geometry of type "Line" - */ -class LineGeometry : public Geometry { -public: - /** The class constructor */ - LineGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc); - - /** The class destructor */ - virtual ~LineGeometry(); - - /** Get a list of all vertex points, non-unique*/ - const std::vector<Vector3> &GetVertices() const; - - /** Return list of vertex indices. */ - const std::vector<int> &GetIndices() const; - -private: - std::vector<Vector3> m_vertices; - std::vector<int> m_indices; -}; -} // namespace FBXDocParser - -#endif // FBX_MESH_GEOMETRY_H diff --git a/modules/fbx/fbx_parser/FBXModel.cpp b/modules/fbx/fbx_parser/FBXModel.cpp deleted file mode 100644 index 03c9de0c35..0000000000 --- a/modules/fbx/fbx_parser/FBXModel.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/*************************************************************************/ -/* FBXModel.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXModel.cpp - * @brief Assimp::FBX::Model implementation - */ - -#include "FBXDocument.h" -#include "FBXDocumentUtil.h" -#include "FBXMeshGeometry.h" -#include "FBXParser.h" - -namespace FBXDocParser { - -using namespace Util; - -// ------------------------------------------------------------------------------------------------ -Model::Model(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : - Object(id, element, name), shading("Y") { - const ScopePtr sc = GetRequiredScope(element); - const ElementPtr Shading = sc->GetElement("Shading"); - const ElementPtr Culling = sc->GetElement("Culling"); - - if (Shading) { - shading = GetRequiredToken(Shading, 0)->StringContents(); - } - - if (Culling) { - culling = ParseTokenAsString(GetRequiredToken(Culling, 0)); - } - - ResolveLinks(element, doc); -} - -// ------------------------------------------------------------------------------------------------ -Model::~Model() { -} - -ModelLimbNode::ModelLimbNode(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : - Model(id, element, doc, name){}; - -ModelLimbNode::~ModelLimbNode() { -} - -// ------------------------------------------------------------------------------------------------ -void Model::ResolveLinks(const ElementPtr element, const Document &doc) { - const char *const arr[] = { "Geometry", "Material", "NodeAttribute" }; - - // resolve material - const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), arr, 3); - - materials.reserve(conns.size()); - geometry.reserve(conns.size()); - attributes.reserve(conns.size()); - for (const Connection *con : conns) { - // material and geometry links should be Object-Object connections - if (con->PropertyName().length()) { - continue; - } - - const Object *const ob = con->SourceObject(); - if (!ob) { - //DOMWarning("failed to read source object for incoming Model link, ignoring",&element); - continue; - } - - const Material *const mat = dynamic_cast<const Material *>(ob); - if (mat) { - materials.push_back(mat); - continue; - } - - const Geometry *const geo = dynamic_cast<const Geometry *>(ob); - if (geo) { - geometry.push_back(geo); - continue; - } - - const NodeAttribute *const att = dynamic_cast<const NodeAttribute *>(ob); - if (att) { - attributes.push_back(att); - continue; - } - - DOMWarning("source object for model link is neither Material, NodeAttribute nor Geometry, ignoring", element); - continue; - } -} - -// ------------------------------------------------------------------------------------------------ -bool Model::IsNull() const { - const std::vector<const NodeAttribute *> &attrs = GetAttributes(); - for (const NodeAttribute *att : attrs) { - const Null *null_tag = dynamic_cast<const Null *>(att); - if (null_tag) { - return true; - } - } - - return false; -} -} // namespace FBXDocParser diff --git a/modules/fbx/fbx_parser/FBXNodeAttribute.cpp b/modules/fbx/fbx_parser/FBXNodeAttribute.cpp deleted file mode 100644 index 15184a0f5d..0000000000 --- a/modules/fbx/fbx_parser/FBXNodeAttribute.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/*************************************************************************/ -/* FBXNodeAttribute.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXNoteAttribute.cpp - * @brief Assimp::FBX::NodeAttribute (and subclasses) implementation - */ - -#include "FBXDocument.h" -#include "FBXDocumentUtil.h" -#include "FBXParser.h" -#include <iostream> - -namespace FBXDocParser { -using namespace Util; - -// ------------------------------------------------------------------------------------------------ -NodeAttribute::NodeAttribute(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : - Object(id, element, name) { -} - -// ------------------------------------------------------------------------------------------------ -NodeAttribute::~NodeAttribute() { - // empty -} - -// ------------------------------------------------------------------------------------------------ -CameraSwitcher::CameraSwitcher(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : - NodeAttribute(id, element, doc, name) { - const ScopePtr sc = GetRequiredScope(element); - const ElementPtr CameraId = sc->GetElement("CameraId"); - const ElementPtr CameraName = sc->GetElement("CameraName"); - const ElementPtr CameraIndexName = sc->GetElement("CameraIndexName"); - - if (CameraId) { - cameraId = ParseTokenAsInt(GetRequiredToken(CameraId, 0)); - } - - if (CameraName) { - cameraName = GetRequiredToken(CameraName, 0)->StringContents(); - } - - if (CameraIndexName && CameraIndexName->Tokens().size()) { - cameraIndexName = GetRequiredToken(CameraIndexName, 0)->StringContents(); - } -} - -// ------------------------------------------------------------------------------------------------ -CameraSwitcher::~CameraSwitcher() { - // empty -} - -// ------------------------------------------------------------------------------------------------ -Camera::Camera(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : - NodeAttribute(id, element, doc, name) { - // empty -} - -// ------------------------------------------------------------------------------------------------ -Camera::~Camera() { - // empty -} - -// ------------------------------------------------------------------------------------------------ -Light::Light(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : - NodeAttribute(id, element, doc, name) { - // empty -} - -// ------------------------------------------------------------------------------------------------ -Light::~Light() { -} - -// ------------------------------------------------------------------------------------------------ -Null::Null(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : - NodeAttribute(id, element, doc, name) { -} - -// ------------------------------------------------------------------------------------------------ -Null::~Null() { -} - -// ------------------------------------------------------------------------------------------------ -LimbNode::LimbNode(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : - NodeAttribute(id, element, doc, name) { - //std::cout << "limb node: " << name << std::endl; - //const Scope &sc = GetRequiredScope(element); - - //const ElementPtr const TypeFlag = sc["TypeFlags"]; - - // keep this it can dump new properties for you - // for( auto element : sc.Elements()) - // { - // std::cout << "limbnode element: " << element.first << std::endl; - // } - - // if(TypeFlag) - // { - // // std::cout << "type flag: " << GetRequiredToken(*TypeFlag, 0).StringContents() << std::endl; - // } -} - -// ------------------------------------------------------------------------------------------------ -LimbNode::~LimbNode() { -} -} // namespace FBXDocParser diff --git a/modules/fbx/fbx_parser/FBXParseTools.h b/modules/fbx/fbx_parser/FBXParseTools.h deleted file mode 100644 index b4003bbec5..0000000000 --- a/modules/fbx/fbx_parser/FBXParseTools.h +++ /dev/null @@ -1,111 +0,0 @@ -/*************************************************************************/ -/* FBXParseTools.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef FBX_PARSE_TOOLS_H -#define FBX_PARSE_TOOLS_H - -#include "core/error/error_macros.h" -#include "core/string/ustring.h" - -#include <stdint.h> -#include <algorithm> -#include <locale> - -template <class char_t> -inline bool IsNewLine(char_t c) { - return c == '\n' || c == '\r'; -} -template <class char_t> -inline bool IsSpace(char_t c) { - return (c == (char_t)' ' || c == (char_t)'\t'); -} - -template <class char_t> -inline bool IsSpaceOrNewLine(char_t c) { - return IsNewLine(c) || IsSpace(c); -} - -template <class char_t> -inline bool IsLineEnd(char_t c) { - return (c == (char_t)'\r' || c == (char_t)'\n' || c == (char_t)'\0' || c == (char_t)'\f'); -} - -// ------------------------------------------------------------------------------------ -// Special version of the function, providing higher accuracy and safety -// It is mainly used by fast_atof to prevent ugly and unwanted integer overflows. -// ------------------------------------------------------------------------------------ -inline uint64_t strtoul10_64(const char *in, bool &errored, const char **out = nullptr, unsigned int *max_inout = nullptr) { - unsigned int cur = 0; - uint64_t value = 0; - - errored = *in < '0' || *in > '9'; - ERR_FAIL_COND_V_MSG(errored, 0, "The string cannot be converted parser error"); - - for (;;) { - if (*in < '0' || *in > '9') { - break; - } - - const uint64_t new_value = (value * (uint64_t)10) + ((uint64_t)(*in - '0')); - - // numeric overflow, we rely on you - if (new_value < value) { - //WARN_PRINT( "Converting the string \" " + in + " \" into a value resulted in overflow." ); - return 0; - } - - value = new_value; - - ++in; - ++cur; - - if (max_inout && *max_inout == cur) { - if (out) { /* skip to end */ - while (*in >= '0' && *in <= '9') { - ++in; - } - *out = in; - } - - return value; - } - } - if (out) { - *out = in; - } - - if (max_inout) { - *max_inout = cur; - } - - return value; -} - -#endif // FBX_PARSE_TOOLS_H diff --git a/modules/fbx/fbx_parser/FBXParser.cpp b/modules/fbx/fbx_parser/FBXParser.cpp deleted file mode 100644 index dbc9a0e46d..0000000000 --- a/modules/fbx/fbx_parser/FBXParser.cpp +++ /dev/null @@ -1,1322 +0,0 @@ -/*************************************************************************/ -/* FBXParser.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXParser.cpp - * @brief Implementation of the FBX parser and the rudimentary DOM that we use - */ - -#include <stdlib.h> /* strtol */ -#include <zlib.h> - -#include "ByteSwapper.h" -#include "FBXParseTools.h" -#include "FBXParser.h" -#include "FBXTokenizer.h" -#include "core/math/math_defs.h" -#include "core/math/transform_3d.h" -#include "core/math/vector3.h" -#include "core/string/print_string.h" - -using namespace FBXDocParser; -namespace { - -// Initially, we did reinterpret_cast, breaking strict aliasing rules. -// This actually caused trouble on Android, so let's be safe this time. -// https://github.com/assimp/assimp/issues/24 -template <typename T> -T SafeParse(const char *data, const char *end) { - // Actual size validation happens during Tokenization so - // this is valid as an assertion. - (void)(end); - //ai_assert(static_cast<size_t>(end - data) >= sizeof(T)); - T result = static_cast<T>(0); - ::memcpy(&result, data, sizeof(T)); - return result; -} -} // namespace - -namespace FBXDocParser { - -// ------------------------------------------------------------------------------------------------ -Element::Element(const TokenPtr key_token, Parser &parser) : - key_token(key_token) { - TokenPtr n = nullptr; - do { - n = parser.AdvanceToNextToken(); - if (n == nullptr) { - continue; - } - - if (!n) { - print_error("unexpected end of file, expected closing bracket" + String(parser.LastToken()->StringContents().c_str())); - } - - if (n && n->Type() == TokenType_DATA) { - tokens.push_back(n); - TokenPtr prev = n; - n = parser.AdvanceToNextToken(); - - if (n == nullptr) { - break; - } - - 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(); - - // some exporters are missing a comma on the next line - if (ty == TokenType_DATA && prev->Type() == TokenType_DATA && (n->Line() == prev->Line() + 1)) { - tokens.push_back(n); - continue; - } - - 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; - } - } - - if (n && n->Type() == TokenType_OPEN_BRACKET) { - 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(); - return; - } - } while (n && n->Type() != TokenType_KEY && n->Type() != TokenType_CLOSE_BRACKET); -} - -// ------------------------------------------------------------------------------------------------ -Element::~Element() { -} - -// ------------------------------------------------------------------------------------------------ -Scope::Scope(Parser &parser, bool topLevel) { - if (!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))); - - // Element() should stop at the next Key token (or right after a Close token) - n = parser.CurrentToken(); - if (n == nullptr) { - if (topLevel) { - return; - } - - //print_error("unexpected end of file" + String(parser.LastToken()->StringContents().c_str())); - } - } -} - -// ------------------------------------------------------------------------------------------------ -Scope::~Scope() { - for (ElementMap::value_type &v : elements) { - delete v.second; - v.second = nullptr; - } - - elements.clear(); -} - -// ------------------------------------------------------------------------------------------------ -Parser::Parser(const TokenList &tokens, bool is_binary) : - corrupt(false), tokens(tokens), cursor(tokens.begin()), is_binary(is_binary) { - root = new_Scope(*this, true); - scopes.push_back(root); -} - -// ------------------------------------------------------------------------------------------------ -Parser::~Parser() { - for (ScopePtr scope : scopes) { - delete scope; - scope = nullptr; - } -} - -// ------------------------------------------------------------------------------------------------ -TokenPtr Parser::AdvanceToNextToken() { - last = current; - if (cursor == tokens.end()) { - current = nullptr; - } else { - current = *cursor++; - } - return current; -} - -// ------------------------------------------------------------------------------------------------ -TokenPtr Parser::CurrentToken() const { - return current; -} - -// ------------------------------------------------------------------------------------------------ -TokenPtr Parser::LastToken() const { - return last; -} - -// ------------------------------------------------------------------------------------------------ -uint64_t ParseTokenAsID(const TokenPtr t, const char *&err_out) { - ERR_FAIL_COND_V_MSG(t == nullptr, 0L, "Invalid token passed to ParseTokenAsID"); - err_out = nullptr; - - if (t->Type() != TokenType_DATA) { - err_out = "expected TOK_DATA token"; - return 0L; - } - - if (t->IsBinary()) { - const char *data = t->begin(); - if (data[0] != 'L') { - err_out = "failed to parse ID, unexpected data type, expected L(ong) (binary)"; - return 0L; - } - - uint64_t id = SafeParse<uint64_t>(data + 1, t->end()); - return id; - } - - // XXX: should use size_t here - unsigned int length = static_cast<unsigned int>(t->end() - t->begin()); - //ai_assert(length > 0); - - const char *out = nullptr; - bool errored = false; - - const uint64_t id = strtoul10_64(t->begin(), errored, &out, &length); - if (errored || out > t->end()) { - err_out = "failed to parse ID (text)"; - return 0L; - } - - return id; -} - -// ------------------------------------------------------------------------------------------------ -// wrapper around ParseTokenAsID() with print_error handling -uint64_t ParseTokenAsID(const TokenPtr t) { - const char *err = nullptr; - const uint64_t i = ParseTokenAsID(t, err); - if (err) { - print_error(String(err) + " " + String(t->StringContents().c_str())); - } - return i; -} - -// ------------------------------------------------------------------------------------------------ -size_t ParseTokenAsDim(const TokenPtr t, const char *&err_out) { - // same as ID parsing, except there is a trailing asterisk - err_out = nullptr; - - if (t->Type() != TokenType_DATA) { - err_out = "expected TOK_DATA token"; - return 0; - } - - if (t->IsBinary()) { - const char *data = t->begin(); - if (data[0] != 'L') { - err_out = "failed to parse ID, unexpected data type, expected L(ong) (binary)"; - return 0; - } - - uint64_t id = SafeParse<uint64_t>(data + 1, t->end()); - AI_SWAP8(id); - return static_cast<size_t>(id); - } - - if (*t->begin() != '*') { - err_out = "expected asterisk before array dimension"; - return 0; - } - - // XXX: should use size_t here - unsigned int length = static_cast<unsigned int>(t->end() - t->begin()); - if (length == 0) { - err_out = "expected valid integer number after asterisk"; - return 0; - } - - const char *out = nullptr; - bool errored = false; - const size_t id = static_cast<size_t>(strtoul10_64(t->begin() + 1, errored, &out, &length)); - if (errored || out > t->end()) { - print_error("failed to parse id"); - err_out = "failed to parse ID"; - return 0; - } - - return id; -} - -// ------------------------------------------------------------------------------------------------ -float ParseTokenAsFloat(const TokenPtr t, const char *&err_out) { - err_out = nullptr; - - if (t->Type() != TokenType_DATA) { - err_out = "expected TOK_DATA token"; - return 0.0f; - } - - if (t->IsBinary()) { - const char *data = t->begin(); - if (data[0] != 'F' && data[0] != 'D') { - err_out = "failed to parse F(loat) or D(ouble), unexpected data type (binary)"; - return 0.0f; - } - - if (data[0] == 'F') { - return SafeParse<float>(data + 1, t->end()); - } else { - return static_cast<float>(SafeParse<double>(data + 1, t->end())); - } - } - -// need to copy the input string to a temporary buffer -// first - next in the fbx token stream comes ',', -// which fast_atof could interpret as decimal point. -#define MAX_FLOAT_LENGTH 31 - char temp[MAX_FLOAT_LENGTH + 1]; - const size_t length = static_cast<size_t>(t->end() - t->begin()); - std::copy(t->begin(), t->end(), temp); - temp[std::min(static_cast<size_t>(MAX_FLOAT_LENGTH), length)] = '\0'; - - return atof(temp); -} - -// ------------------------------------------------------------------------------------------------ -int ParseTokenAsInt(const TokenPtr t, const char *&err_out) { - err_out = nullptr; - - if (t->Type() != TokenType_DATA) { - err_out = "expected TOK_DATA token"; - return 0; - } - - // binary files are simple to parse - if (t->IsBinary()) { - const char *data = t->begin(); - if (data[0] != 'I') { - err_out = "failed to parse I(nt), unexpected data type (binary)"; - return 0; - } - - int32_t ival = SafeParse<int32_t>(data + 1, t->end()); - AI_SWAP4(ival); - return static_cast<int>(ival); - } - - // ASCII files are unsafe. - const size_t length = static_cast<size_t>(t->end() - t->begin()); - if (length == 0) { - err_out = "expected valid integer number after asterisk"; - ERR_FAIL_V_MSG(0, "expected valid integer number after asterisk"); - } - - // must not be null for strtol to work - char *out = (char *)t->end(); - // string begin, end ptr ref, base 10 - const int value = strtol(t->begin(), &out, 10); - if (out == nullptr || out != t->end()) { - err_out = "failed to parse ID"; - ERR_FAIL_V_MSG(0, "failed to parse ID"); - } - - return value; -} - -// ------------------------------------------------------------------------------------------------ -int64_t ParseTokenAsInt64(const TokenPtr t, const char *&err_out) { - err_out = nullptr; - - if (t->Type() != TokenType_DATA) { - err_out = "expected TOK_DATA token"; - return 0L; - } - - if (t->IsBinary()) { - const char *data = t->begin(); - if (data[0] != 'L') { - err_out = "failed to parse Int64, unexpected data type"; - return 0L; - } - - int64_t id = SafeParse<int64_t>(data + 1, t->end()); - AI_SWAP8(id); - return id; - } - - // XXX: should use size_t here - unsigned int length = static_cast<unsigned int>(t->end() - t->begin()); - //ai_assert(length > 0); - - char *out = nullptr; - const int64_t id = strtol(t->begin(), &out, length); - if (out > t->end()) { - err_out = "failed to parse Int64 (text)"; - return 0L; - } - - return id; -} - -// ------------------------------------------------------------------------------------------------ -std::string ParseTokenAsString(const TokenPtr t, const char *&err_out) { - err_out = nullptr; - - if (t->Type() != TokenType_DATA) { - err_out = "expected TOK_DATA token"; - return ""; - } - - if (t->IsBinary()) { - const char *data = t->begin(); - if (data[0] != 'S') { - err_out = "failed to parse String, unexpected data type (binary)"; - return ""; - } - - // read string length - int32_t len = SafeParse<int32_t>(data + 1, t->end()); - AI_SWAP4(len); - - //ai_assert(t.end() - data == 5 + len); - return std::string(data + 5, len); - } - - const size_t length = static_cast<size_t>(t->end() - t->begin()); - if (length < 2) { - err_out = "token is too short to hold a string"; - return ""; - } - - const char *s = t->begin(), *e = t->end() - 1; - if (*s != '\"' || *e != '\"') { - err_out = "expected double quoted string"; - return ""; - } - - return std::string(s + 1, length - 2); -} - -namespace { - -// ------------------------------------------------------------------------------------------------ -// read the type code and element count of a binary data array and stop there -void ReadBinaryDataArrayHead(const char *&data, const char *end, char &type, uint32_t &count, - const ElementPtr el) { - TokenPtr token = el->KeyToken(); - if (static_cast<size_t>(end - data) < 5) { - print_error("binary data array is too short, need five (5) bytes for type signature and element count: " + String(token->StringContents().c_str())); - } - - // data type - type = *data; - - // read number of elements - uint32_t len = SafeParse<uint32_t>(data + 1, end); - AI_SWAP4(len); - - count = len; - data += 5; -} - -// ------------------------------------------------------------------------------------------------ -// read binary data array, assume cursor points to the 'compression mode' field (i.e. behind the header) -void ReadBinaryDataArray(char type, uint32_t count, const char *&data, const char *end, - std::vector<char> &buff, - const ElementPtr /*el*/) { - uint32_t encmode = SafeParse<uint32_t>(data, end); - AI_SWAP4(encmode); - data += 4; - - // next comes the compressed length - uint32_t comp_len = SafeParse<uint32_t>(data, end); - AI_SWAP4(comp_len); - data += 4; - - //ai_assert(data + comp_len == end); - - // determine the length of the uncompressed data by looking at the type signature - uint32_t stride = 0; - switch (type) { - case 'f': - case 'i': - stride = 4; - break; - - case 'd': - case 'l': - stride = 8; - break; - } - - const uint32_t full_length = stride * count; - buff.resize(full_length); - - if (encmode == 0) { - //ai_assert(full_length == comp_len); - - // plain data, no compression - std::copy(data, end, buff.begin()); - } else if (encmode == 1) { - // zlib/deflate, next comes ZIP head (0x78 0x01) - // see https://www.ietf.org/rfc/rfc1950.txt - - z_stream zstream; - zstream.opaque = Z_NULL; - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.data_type = Z_BINARY; - - // http://hewgill.com/journal/entries/349-how-to-decompress-gzip-stream-with-zlib - if (Z_OK != inflateInit(&zstream)) { - print_error("failure initializing zlib"); - } - - zstream.next_in = reinterpret_cast<Bytef *>(const_cast<char *>(data)); - zstream.avail_in = comp_len; - - zstream.avail_out = static_cast<uInt>(buff.size()); - zstream.next_out = reinterpret_cast<Bytef *>(&*buff.begin()); - const int ret = inflate(&zstream, Z_FINISH); - - if (ret != Z_STREAM_END && ret != Z_OK) { - print_error("failure decompressing compressed data section"); - } - - // terminate zlib - inflateEnd(&zstream); - } -#ifdef ASSIMP_BUILD_DEBUG - else { - // runtime check for this happens at tokenization stage - //ai_assert(false); - } -#endif - - data += comp_len; - //ai_assert(data == end); -} -} // namespace - -// ------------------------------------------------------------------------------------------------ -// read an array of float3 tuples -void ParseVectorDataArray(std::vector<Vector3> &out, const ElementPtr el) { - out.resize(0); - - const TokenList &tok = el->Tokens(); - TokenPtr token = el->KeyToken(); - if (tok.empty()) { - print_error("unexpected empty element" + String(token->StringContents().c_str())); - } - - if (tok[0]->IsBinary()) { - const char *data = tok[0]->begin(), *end = tok[0]->end(); - - char type; - uint32_t count; - ReadBinaryDataArrayHead(data, end, type, count, el); - - if (count % 3 != 0) { - print_error("number of floats is not a multiple of three (3) (binary)" + String(token->StringContents().c_str())); - } - - if (!count) { - return; - } - - if (type != 'd' && type != 'f') { - print_error("expected float or double array (binary)" + String(token->StringContents().c_str())); - } - - std::vector<char> buff; - ReadBinaryDataArray(type, count, data, end, buff, el); - - //ai_assert(data == end); - //ai_assert(buff.size() == count * (type == 'd' ? 8 : 4)); - - const uint32_t count3 = count / 3; - out.reserve(count3); - - if (type == 'd') { - const double *d = reinterpret_cast<const double *>(&buff[0]); - for (unsigned int i = 0; i < count3; ++i, d += 3) { - out.push_back(Vector3(static_cast<real_t>(d[0]), - static_cast<real_t>(d[1]), - static_cast<real_t>(d[2]))); - } - } else if (type == 'f') { - const float *f = reinterpret_cast<const float *>(&buff[0]); - for (unsigned int i = 0; i < count3; ++i, f += 3) { - out.push_back(Vector3(f[0], f[1], f[2])); - } - } - - return; - } - - const size_t dim = ParseTokenAsDim(tok[0]); - - // may throw bad_alloc if the input is rubbish, but this need - // not to be prevented - importing would fail but we wouldn't - // crash since assimp handles this case properly. - out.reserve(dim); - - const ScopePtr scope = GetRequiredScope(el); - const ElementPtr a = GetRequiredElement(scope, "a", el); - - if (a->Tokens().size() % 3 != 0) { - print_error("number of floats is not a multiple of three (3)" + String(token->StringContents().c_str())); - } else { - for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) { - Vector3 v; - v.x = ParseTokenAsFloat(*it++); - v.y = ParseTokenAsFloat(*it++); - v.z = ParseTokenAsFloat(*it++); - - out.push_back(v); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// read an array of color4 tuples -void ParseVectorDataArray(std::vector<Color> &out, const ElementPtr el) { - out.resize(0); - const TokenList &tok = el->Tokens(); - - TokenPtr token = el->KeyToken(); - - if (tok.empty()) { - print_error("unexpected empty element" + String(token->StringContents().c_str())); - } - - if (tok[0]->IsBinary()) { - const char *data = tok[0]->begin(), *end = tok[0]->end(); - - char type; - uint32_t count; - ReadBinaryDataArrayHead(data, end, type, count, el); - - if (count % 4 != 0) { - print_error("number of floats is not a multiple of four (4) (binary)" + String(token->StringContents().c_str())); - } - - if (!count) { - return; - } - - if (type != 'd' && type != 'f') { - print_error("expected float or double array (binary)" + String(token->StringContents().c_str())); - } - - std::vector<char> buff; - ReadBinaryDataArray(type, count, data, end, buff, el); - - //ai_assert(data == end); - //ai_assert(buff.size() == count * (type == 'd' ? 8 : 4)); - - const uint32_t count4 = count / 4; - out.reserve(count4); - - if (type == 'd') { - const double *d = reinterpret_cast<const double *>(&buff[0]); - for (unsigned int i = 0; i < count4; ++i, d += 4) { - out.push_back(Color(static_cast<float>(d[0]), - static_cast<float>(d[1]), - static_cast<float>(d[2]), - static_cast<float>(d[3]))); - } - } else if (type == 'f') { - const float *f = reinterpret_cast<const float *>(&buff[0]); - for (unsigned int i = 0; i < count4; ++i, f += 4) { - out.push_back(Color(f[0], f[1], f[2], f[3])); - } - } - return; - } - - const size_t dim = ParseTokenAsDim(tok[0]); - - // see notes in ParseVectorDataArray() above - out.reserve(dim); - - const ScopePtr scope = GetRequiredScope(el); - const ElementPtr a = GetRequiredElement(scope, "a", el); - - if (a->Tokens().size() % 4 != 0) { - print_error("number of floats is not a multiple of four (4)" + String(token->StringContents().c_str())); - } - for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) { - Color v; - v.r = ParseTokenAsFloat(*it++); - v.g = ParseTokenAsFloat(*it++); - v.b = ParseTokenAsFloat(*it++); - v.a = ParseTokenAsFloat(*it++); - - out.push_back(v); - } -} - -// ------------------------------------------------------------------------------------------------ -// read an array of float2 tuples -void ParseVectorDataArray(std::vector<Vector2> &out, const ElementPtr el) { - out.resize(0); - const TokenList &tok = el->Tokens(); - TokenPtr token = el->KeyToken(); - if (tok.empty()) { - print_error("unexpected empty element" + String(token->StringContents().c_str())); - } - - if (tok[0]->IsBinary()) { - const char *data = tok[0]->begin(), *end = tok[0]->end(); - - char type; - uint32_t count; - ReadBinaryDataArrayHead(data, end, type, count, el); - - if (count % 2 != 0) { - print_error("number of floats is not a multiple of two (2) (binary)" + String(token->StringContents().c_str())); - } - - if (!count) { - return; - } - - if (type != 'd' && type != 'f') { - print_error("expected float or double array (binary)" + String(token->StringContents().c_str())); - } - - std::vector<char> buff; - ReadBinaryDataArray(type, count, data, end, buff, el); - - //ai_assert(data == end); - //ai_assert(buff.size() == count * (type == 'd' ? 8 : 4)); - - const uint32_t count2 = count / 2; - out.reserve(count2); - - if (type == 'd') { - const double *d = reinterpret_cast<const double *>(&buff[0]); - for (unsigned int i = 0; i < count2; ++i, d += 2) { - out.push_back(Vector2(static_cast<float>(d[0]), - static_cast<float>(d[1]))); - } - } else if (type == 'f') { - const float *f = reinterpret_cast<const float *>(&buff[0]); - for (unsigned int i = 0; i < count2; ++i, f += 2) { - out.push_back(Vector2(f[0], f[1])); - } - } - - return; - } - - const size_t dim = ParseTokenAsDim(tok[0]); - - // see notes in ParseVectorDataArray() above - out.reserve(dim); - - const ScopePtr scope = GetRequiredScope(el); - const ElementPtr a = GetRequiredElement(scope, "a", el); - - if (a->Tokens().size() % 2 != 0) { - print_error("number of floats is not a multiple of two (2)" + String(token->StringContents().c_str())); - } else { - for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) { - Vector2 v; - v.x = ParseTokenAsFloat(*it++); - v.y = ParseTokenAsFloat(*it++); - out.push_back(v); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// read an array of ints -void ParseVectorDataArray(std::vector<int> &out, const ElementPtr el) { - out.resize(0); - const TokenList &tok = el->Tokens(); - TokenPtr token = el->KeyToken(); - if (tok.empty()) { - print_error("unexpected empty element" + String(token->StringContents().c_str())); - } - - if (tok[0]->IsBinary()) { - const char *data = tok[0]->begin(), *end = tok[0]->end(); - - char type; - uint32_t count; - ReadBinaryDataArrayHead(data, end, type, count, el); - - if (!count) { - return; - } - - if (type != 'i') { - print_error("expected int array (binary)" + String(token->StringContents().c_str())); - } - - std::vector<char> buff; - ReadBinaryDataArray(type, count, data, end, buff, el); - - //ai_assert(data == end); - //ai_assert(buff.size() == count * 4); - - out.reserve(count); - - const int32_t *ip = reinterpret_cast<const int32_t *>(&buff[0]); - for (unsigned int i = 0; i < count; ++i, ++ip) { - int32_t val = *ip; - AI_SWAP4(val); - out.push_back(val); - } - - return; - } - - const size_t dim = ParseTokenAsDim(tok[0]); - - // see notes in ParseVectorDataArray() - out.reserve(dim); - - const ScopePtr scope = GetRequiredScope(el); - const ElementPtr a = GetRequiredElement(scope, "a", el); - - for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) { - const int ival = ParseTokenAsInt(*it++); - out.push_back(ival); - } -} - -// ------------------------------------------------------------------------------------------------ -// read an array of floats -void ParseVectorDataArray(std::vector<float> &out, const ElementPtr el) { - out.resize(0); - const TokenList &tok = el->Tokens(); - TokenPtr token = el->KeyToken(); - if (tok.empty()) { - print_error("unexpected empty element: " + String(token->StringContents().c_str())); - } - - if (tok[0]->IsBinary()) { - const char *data = tok[0]->begin(), *end = tok[0]->end(); - - char type; - uint32_t count; - ReadBinaryDataArrayHead(data, end, type, count, el); - - if (!count) { - return; - } - - if (type != 'd' && type != 'f') { - print_error("expected float or double array (binary) " + String(token->StringContents().c_str())); - } - - std::vector<char> buff; - ReadBinaryDataArray(type, count, data, end, buff, el); - - //ai_assert(data == end); - //ai_assert(buff.size() == count * (type == 'd' ? 8 : 4)); - - if (type == 'd') { - const double *d = reinterpret_cast<const double *>(&buff[0]); - for (unsigned int i = 0; i < count; ++i, ++d) { - out.push_back(static_cast<float>(*d)); - } - } else if (type == 'f') { - const float *f = reinterpret_cast<const float *>(&buff[0]); - for (unsigned int i = 0; i < count; ++i, ++f) { - out.push_back(*f); - } - } - - return; - } - - const size_t dim = ParseTokenAsDim(tok[0]); - - // see notes in ParseVectorDataArray() - out.reserve(dim); - - const ScopePtr scope = GetRequiredScope(el); - const ElementPtr a = GetRequiredElement(scope, "a", el); - - for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) { - const float ival = ParseTokenAsFloat(*it++); - out.push_back(ival); - } -} - -// ------------------------------------------------------------------------------------------------ -// read an array of uints -void ParseVectorDataArray(std::vector<unsigned int> &out, const ElementPtr el) { - out.resize(0); - const TokenList &tok = el->Tokens(); - const TokenPtr token = el->KeyToken(); - - ERR_FAIL_COND_MSG(!token, "invalid ParseVectorDataArrat token invalid"); - - if (tok.empty()) { - print_error("unexpected empty element: " + String(token->StringContents().c_str())); - } - - if (tok[0]->IsBinary()) { - const char *data = tok[0]->begin(), *end = tok[0]->end(); - - char type; - uint32_t count; - ReadBinaryDataArrayHead(data, end, type, count, el); - - if (!count) { - return; - } - - if (type != 'i') { - print_error("expected (u)int array (binary)" + String(token->StringContents().c_str())); - } - - std::vector<char> buff; - ReadBinaryDataArray(type, count, data, end, buff, el); - - //ai_assert(data == end); - //ai_assert(buff.size() == count * 4); - - out.reserve(count); - - const int32_t *ip = reinterpret_cast<const int32_t *>(&buff[0]); - for (unsigned int i = 0; i < count; ++i, ++ip) { - int32_t val = *ip; - if (val < 0) { - print_error("encountered negative integer index (binary)"); - } - - out.push_back(val); - } - - return; - } - - const size_t dim = ParseTokenAsDim(tok[0]); - - // see notes in ParseVectorDataArray() - out.reserve(dim); - - const ScopePtr scope = GetRequiredScope(el); - const ElementPtr a = GetRequiredElement(scope, "a", el); - - for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) { - const int ival = ParseTokenAsInt(*it++); - if (ival < 0) { - print_error("encountered negative integer index"); - } - out.push_back(static_cast<unsigned int>(ival)); - } -} - -// ------------------------------------------------------------------------------------------------ -// read an array of uint64_ts -void ParseVectorDataArray(std::vector<uint64_t> &out, const ElementPtr el) { - out.resize(0); - - const TokenList &tok = el->Tokens(); - TokenPtr token = el->KeyToken(); - ERR_FAIL_COND(!token); - - if (tok.empty()) { - print_error("unexpected empty element " + String(token->StringContents().c_str())); - } - - if (tok[0]->IsBinary()) { - const char *data = tok[0]->begin(), *end = tok[0]->end(); - - char type; - uint32_t count; - ReadBinaryDataArrayHead(data, end, type, count, el); - - if (!count) { - return; - } - - if (type != 'l') { - print_error("expected long array (binary): " + String(token->StringContents().c_str())); - } - - std::vector<char> buff; - ReadBinaryDataArray(type, count, data, end, buff, el); - - //ai_assert(data == end); - //ai_assert(buff.size() == count * 8); - - out.reserve(count); - - const uint64_t *ip = reinterpret_cast<const uint64_t *>(&buff[0]); - for (unsigned int i = 0; i < count; ++i, ++ip) { - uint64_t val = *ip; - AI_SWAP8(val); - out.push_back(val); - } - - return; - } - - const size_t dim = ParseTokenAsDim(tok[0]); - - // see notes in ParseVectorDataArray() - out.reserve(dim); - - const ScopePtr scope = GetRequiredScope(el); - const ElementPtr a = GetRequiredElement(scope, "a", el); - - for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) { - const uint64_t ival = ParseTokenAsID(*it++); - - out.push_back(ival); - } -} - -// ------------------------------------------------------------------------------------------------ -// read an array of int64_ts -void ParseVectorDataArray(std::vector<int64_t> &out, const ElementPtr el) { - out.resize(0); - const TokenList &tok = el->Tokens(); - TokenPtr token = el->KeyToken(); - ERR_FAIL_COND(!token); - if (tok.empty()) { - print_error("unexpected empty element: " + String(token->StringContents().c_str())); - } - - if (tok[0]->IsBinary()) { - const char *data = tok[0]->begin(), *end = tok[0]->end(); - - char type; - uint32_t count; - ReadBinaryDataArrayHead(data, end, type, count, el); - - if (!count) { - return; - } - - if (type != 'l') { - print_error("expected long array (binary) " + String(token->StringContents().c_str())); - } - - std::vector<char> buff; - ReadBinaryDataArray(type, count, data, end, buff, el); - - //ai_assert(data == end); - //ai_assert(buff.size() == count * 8); - - out.reserve(count); - - const int64_t *ip = reinterpret_cast<const int64_t *>(&buff[0]); - for (unsigned int i = 0; i < count; ++i, ++ip) { - int64_t val = *ip; - AI_SWAP8(val); - out.push_back(val); - } - - return; - } - - const size_t dim = ParseTokenAsDim(tok[0]); - - // see notes in ParseVectorDataArray() - out.reserve(dim); - - const ScopePtr scope = GetRequiredScope(el); - const ElementPtr a = GetRequiredElement(scope, "a", el); - - for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) { - const int64_t val = ParseTokenAsInt64(*it++); - out.push_back(val); - } -} - -// ------------------------------------------------------------------------------------------------ -Transform3D ReadMatrix(const ElementPtr element) { - std::vector<float> values; - ParseVectorDataArray(values, element); - - if (values.size() != 16) { - print_error("expected 16 matrix elements"); - } - - // clean values to prevent any IBM damage on inverse() / affine_inverse() - for (float &value : values) { - if (::Math::is_zero_approx(value)) { - value = 0; - } - } - - Transform3D xform; - Basis basis; - - basis.set( - Vector3(values[0], values[1], values[2]), - Vector3(values[4], values[5], values[6]), - Vector3(values[8], values[9], values[10])); - - xform.basis = basis; - xform.origin = Vector3(values[12], values[13], values[14]); - // determine if we need to think about this with dynamic rotation order? - // for example: - // xform.basis = z_axis * y_axis * x_axis; - //xform.basis.transpose(); - - print_verbose("xform verbose basis: " + (xform.basis.get_euler() * (180 / Math_PI)) + " xform origin:" + xform.origin); - - return xform; -} - -// ------------------------------------------------------------------------------------------------ -// wrapper around ParseTokenAsString() with print_error handling -std::string ParseTokenAsString(const TokenPtr t) { - ERR_FAIL_COND_V(!t, ""); - const char *err; - const std::string &i = ParseTokenAsString(t, err); - if (err) { - print_error(String(err) + ", " + String(t->StringContents().c_str())); - } - return i; -} - -// ------------------------------------------------------------------------------------------------ -// 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*/) { - const ElementPtr el = sc->GetElement(index); - TokenPtr token = el->KeyToken(); - ERR_FAIL_COND_V(!token, nullptr); - if (!el) { - print_error("did not find required element \"" + String(index.c_str()) + "\" " + String(token->StringContents().c_str())); - } - return el; -} - -bool HasElement(const ScopePtr sc, const std::string &index) { - const ElementPtr el = sc->GetElement(index); - if (nullptr == el) { - return false; - } - - return true; -} - -// ------------------------------------------------------------------------------------------------ -// extract a required element from a scope, abort if the element cannot be found -ElementPtr GetOptionalElement(const ScopePtr sc, const std::string &index, const ElementPtr element /*= nullptr*/) { - const ElementPtr el = sc->GetElement(index); - return el; -} - -// ------------------------------------------------------------------------------------------------ -// extract required compound scope -ScopePtr GetRequiredScope(const ElementPtr el) { - if (el) { - ScopePtr s = el->Compound(); - TokenPtr token = el->KeyToken(); - ERR_FAIL_COND_V(!token, nullptr); - if (s) { - return s; - } - - ERR_FAIL_V_MSG(nullptr, "expected compound scope " + String(token->StringContents().c_str())); - } - - 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) { - if (el) { - const TokenList &x = el->Tokens(); - TokenPtr token = el->KeyToken(); - - ERR_FAIL_COND_V(!token, nullptr); - - if (index >= x.size()) { - ERR_FAIL_V_MSG(nullptr, "missing token at index: " + itos(index) + " " + String(token->StringContents().c_str())); - } - - return x[index]; - } - - return nullptr; -} - -// ------------------------------------------------------------------------------------------------ -// wrapper around ParseTokenAsDim() with print_error handling -size_t ParseTokenAsDim(const TokenPtr t) { - const char *err; - const size_t i = ParseTokenAsDim(t, err); - if (err) { - print_error(String(err) + " " + String(t->StringContents().c_str())); - } - return i; -} - -// ------------------------------------------------------------------------------------------------ -// wrapper around ParseTokenAsFloat() with print_error handling -float ParseTokenAsFloat(const TokenPtr t) { - const char *err; - const float i = ParseTokenAsFloat(t, err); - if (err) { - print_error(String(err) + " " + String(t->StringContents().c_str())); - } - return i; -} - -// ------------------------------------------------------------------------------------------------ -// wrapper around ParseTokenAsInt() with print_error handling -int ParseTokenAsInt(const TokenPtr t) { - const char *err; - const int i = ParseTokenAsInt(t, err); - if (err) { - print_error(String(err) + " " + String(t->StringContents().c_str())); - } - return i; -} - -// ------------------------------------------------------------------------------------------------ -// wrapper around ParseTokenAsInt64() with print_error handling -int64_t ParseTokenAsInt64(const TokenPtr t) { - const char *err; - const int64_t i = ParseTokenAsInt64(t, err); - if (err) { - print_error(String(err) + " " + String(t->StringContents().c_str())); - } - return i; -} -} // namespace FBXDocParser diff --git a/modules/fbx/fbx_parser/FBXParser.h b/modules/fbx/fbx_parser/FBXParser.h deleted file mode 100644 index 27db18bf8a..0000000000 --- a/modules/fbx/fbx_parser/FBXParser.h +++ /dev/null @@ -1,270 +0,0 @@ -/*************************************************************************/ -/* FBXParser.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXParser.h - * @brief FBX parsing code - */ -#ifndef FBX_PARSER_H -#define FBX_PARSER_H - -#include <stdint.h> -#include <map> -#include <memory> - -#include "core/math/color.h" -#include "core/math/transform_3d.h" -#include "core/math/vector2.h" -#include "core/math/vector3.h" - -#include "FBXTokenizer.h" - -namespace FBXDocParser { - -class Scope; -class Parser; -class Element; - -typedef Element *ElementPtr; -typedef Scope *ScopePtr; - -typedef std::vector<ScopePtr> ScopeList; -typedef std::multimap<std::string, ElementPtr> ElementMap; -typedef std::pair<ElementMap::const_iterator, ElementMap::const_iterator> ElementCollection; - -#define new_Scope new Scope -#define new_Element new Element - -/** FBX data entity that consists of a key:value tuple. - * - * Example: - * @verbatim - * AnimationCurve: 23, "AnimCurve::", "" { - * [..] - * } - * @endverbatim - * - * As can be seen in this sample, elements can contain nested #Scope - * as their trailing member. **/ -class Element { -public: - Element(TokenPtr key_token, Parser &parser); - ~Element(); - - ScopePtr Compound() const { - return compound; - } - - TokenPtr KeyToken() const { - return key_token; - } - - const TokenList &Tokens() const { - return tokens; - } - -private: - TokenList tokens; - ScopePtr compound = nullptr; - std::vector<ScopePtr> compound_scope; - TokenPtr key_token = nullptr; -}; - -/** FBX data entity that consists of a 'scope', a collection - * of not necessarily unique #Element instances. - * - * Example: - * @verbatim - * GlobalSettings: { - * Version: 1000 - * Properties70: - * [...] - * } - * @endverbatim */ -class Scope { -public: - Scope(Parser &parser, bool topLevel = false); - ~Scope(); - - ElementPtr GetElement(const std::string &index) const { - ElementMap::const_iterator it = elements.find(index); - return it == elements.end() ? nullptr : (*it).second; - } - - ElementPtr FindElementCaseInsensitive(const std::string &elementName) const { - for (FBXDocParser::ElementMap::const_iterator element = elements.begin(); element != elements.end(); ++element) { - if (element->first.compare(elementName)) { - return element->second; - } - } - - // nothing to reference / expired. - return nullptr; - } - - ElementCollection GetCollection(const std::string &index) const { - return elements.equal_range(index); - } - - const ElementMap &Elements() const { - return elements; - } - -private: - ElementMap elements; -}; - -/** FBX parsing class, takes a list of input tokens and generates a hierarchy - * of nested #Scope instances, representing the fbx DOM.*/ -class Parser { -public: - /** Parse given a token list. Does not take ownership of the tokens - - * the objects must persist during the entire parser lifetime */ - Parser(const TokenList &tokens, bool is_binary); - ~Parser(); - - ScopePtr GetRootScope() const { - return root; - } - - bool IsBinary() const { - return is_binary; - } - - bool IsCorrupt() const { - return corrupt; - } - -private: - friend class Scope; - friend class Element; - - TokenPtr AdvanceToNextToken(); - TokenPtr LastToken() const; - TokenPtr CurrentToken() const; - -private: - bool corrupt = false; - ScopeList scopes; - const TokenList &tokens; - - TokenPtr last = nullptr, current = nullptr; - TokenList::const_iterator cursor; - ScopePtr root = nullptr; - - const bool is_binary; -}; - -/* token parsing - this happens when building the DOM out of the parse-tree*/ -uint64_t ParseTokenAsID(const TokenPtr t, const char *&err_out); -size_t ParseTokenAsDim(const TokenPtr t, const char *&err_out); -float ParseTokenAsFloat(const TokenPtr t, const char *&err_out); -int ParseTokenAsInt(const TokenPtr t, const char *&err_out); -int64_t ParseTokenAsInt64(const TokenPtr t, const char *&err_out); -std::string ParseTokenAsString(const TokenPtr t, const char *&err_out); - -/* wrapper around ParseTokenAsXXX() with DOMError handling */ -uint64_t ParseTokenAsID(const TokenPtr t); -size_t ParseTokenAsDim(const TokenPtr t); -float ParseTokenAsFloat(const TokenPtr t); -int ParseTokenAsInt(const TokenPtr t); -int64_t ParseTokenAsInt64(const TokenPtr t); -std::string ParseTokenAsString(const TokenPtr t); - -/* read data arrays */ -void ParseVectorDataArray(std::vector<Vector3> &out, const ElementPtr el); -void ParseVectorDataArray(std::vector<Color> &out, const ElementPtr el); -void ParseVectorDataArray(std::vector<Vector2> &out, const ElementPtr el); -void ParseVectorDataArray(std::vector<int> &out, const ElementPtr el); -void ParseVectorDataArray(std::vector<float> &out, const ElementPtr el); -void ParseVectorDataArray(std::vector<float> &out, const ElementPtr el); -void ParseVectorDataArray(std::vector<unsigned int> &out, const ElementPtr el); -void ParseVectorDataArray(std::vector<uint64_t> &out, const ElementPtr ep); -void ParseVectorDataArray(std::vector<int64_t> &out, const ElementPtr el); -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); -// get token at a particular index -TokenPtr GetRequiredToken(const ElementPtr el, unsigned int index); - -// ------------------------------------------------------------------------------------------------ -// read a 4x4 matrix from an array of 16 floats -Transform3D ReadMatrix(const ElementPtr element); -} // namespace FBXDocParser - -#endif // FBX_PARSER_H diff --git a/modules/fbx/fbx_parser/FBXPose.cpp b/modules/fbx/fbx_parser/FBXPose.cpp deleted file mode 100644 index 6d80b85e38..0000000000 --- a/modules/fbx/fbx_parser/FBXPose.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/*************************************************************************/ -/* FBXPose.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXNoteAttribute.cpp - * @brief Assimp::FBX::NodeAttribute (and subclasses) implementation - */ - -#include "FBXDocument.h" -#include "FBXParser.h" -#include <iostream> - -namespace FBXDocParser { - -class FbxPoseNode; -// ------------------------------------------------------------------------------------------------ -FbxPose::FbxPose(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)); - - const ElementCollection &PoseNodes = sc->GetCollection("PoseNode"); - for (ElementMap::const_iterator it = PoseNodes.first; it != PoseNodes.second; ++it) { - std::string entry_name = (*it).first; - ElementPtr some_element = (*it).second; - FbxPoseNode *pose_node = new FbxPoseNode(some_element, doc, entry_name); - pose_nodes.push_back(pose_node); - } -} - -// ------------------------------------------------------------------------------------------------ -FbxPose::~FbxPose() { - pose_nodes.clear(); - // empty -} -} // namespace FBXDocParser diff --git a/modules/fbx/fbx_parser/FBXProperties.cpp b/modules/fbx/fbx_parser/FBXProperties.cpp deleted file mode 100644 index b8c0f685ac..0000000000 --- a/modules/fbx/fbx_parser/FBXProperties.cpp +++ /dev/null @@ -1,246 +0,0 @@ -/*************************************************************************/ -/* FBXProperties.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXProperties.cpp - * @brief Implementation of the FBX dynamic properties system - */ - -#include "FBXProperties.h" -#include "FBXDocumentUtil.h" -#include "FBXParser.h" -#include "FBXTokenizer.h" - -namespace FBXDocParser { - -using namespace Util; - -// ------------------------------------------------------------------------------------------------ -Property::Property() { -} - -// ------------------------------------------------------------------------------------------------ -Property::~Property() { -} - -namespace { - -// ------------------------------------------------------------------------------------------------ -// read a typed property out of a FBX element. The return value is nullptr if the property cannot be read. -PropertyPtr ReadTypedProperty(const ElementPtr element) { - //ai_assert(element.KeyToken().StringContents() == "P"); - - const TokenList &tok = element->Tokens(); - //ai_assert(tok.size() >= 5); - - const std::string &s = ParseTokenAsString(tok[1]); - const char *const cs = s.c_str(); - if (!strcmp(cs, "KString")) { - return new TypedProperty<std::string>(ParseTokenAsString(tok[4])); - } else if (!strcmp(cs, "bool") || !strcmp(cs, "Bool")) { - return new TypedProperty<bool>(ParseTokenAsInt(tok[4]) != 0); - } else if (!strcmp(cs, "int") || !strcmp(cs, "Int") || !strcmp(cs, "enum") || !strcmp(cs, "Enum")) { - return new TypedProperty<int>(ParseTokenAsInt(tok[4])); - } else if (!strcmp(cs, "ULongLong")) { - return new TypedProperty<uint64_t>(ParseTokenAsID(tok[4])); - } else if (!strcmp(cs, "KTime")) { - return new TypedProperty<int64_t>(ParseTokenAsInt64(tok[4])); - } else if (!strcmp(cs, "Vector3D") || - !strcmp(cs, "ColorRGB") || - !strcmp(cs, "Vector") || - !strcmp(cs, "Color") || - !strcmp(cs, "Lcl Translation") || - !strcmp(cs, "Lcl Rotation") || - !strcmp(cs, "Lcl Scaling")) { - return new TypedProperty<Vector3>(Vector3( - ParseTokenAsFloat(tok[4]), - ParseTokenAsFloat(tok[5]), - ParseTokenAsFloat(tok[6]))); - } else if (!strcmp(cs, "double") || !strcmp(cs, "Number") || !strcmp(cs, "Float") || !strcmp(cs, "float") || !strcmp(cs, "FieldOfView") || !strcmp(cs, "UnitScaleFactor")) { - return new TypedProperty<float>(ParseTokenAsFloat(tok[4])); - } - - return nullptr; -} - -// ------------------------------------------------------------------------------------------------ -// peek into an element and check if it contains a FBX property, if so return its name. -std::string PeekPropertyName(const Element &element) { - //ai_assert(element.KeyToken().StringContents() == "P"); - const TokenList &tok = element.Tokens(); - if (tok.size() < 4) { - return ""; - } - - return ParseTokenAsString(tok[0]); -} -} // namespace - -// ------------------------------------------------------------------------------------------------ -PropertyTable::PropertyTable() : - element(nullptr) { -} - -// Is used when dealing with FBX Objects not metadata. -PropertyTable::PropertyTable(const ElementPtr element) : - element(element) { - Setup(element); -} - -// ------------------------------------------------------------------------------------------------ -PropertyTable::~PropertyTable() { - for (PropertyMap::value_type &v : props) { - delete v.second; - } -} - -void PropertyTable::Setup(ElementPtr ptr) { - const ScopePtr sc = GetRequiredScope(ptr); - const ElementPtr Properties70 = sc->GetElement("Properties70"); - const ScopePtr scope = GetOptionalScope(Properties70); - - // no scope, no care. - if (!scope) { - return; // NOTE: this is not an error this is actually a Object, without properties, here we will nullptr it. - } - - for (const ElementMap::value_type &v : scope->Elements()) { - if (v.first != "P") { - DOMWarning("expected only P elements in property table", v.second); - continue; - } - - const std::string &name = PeekPropertyName(*v.second); - if (!name.length()) { - DOMWarning("could not read property name", v.second); - continue; - } - - LazyPropertyMap::const_iterator it = lazyProps.find(name); - if (it != lazyProps.end()) { - DOMWarning("duplicate property name, will hide previous value: " + name, v.second); - continue; - } - - // since the above checks for duplicates we can be sure to insert the only match here. - lazyProps[name] = v.second; - } -} - -// ------------------------------------------------------------------------------------------------ -PropertyPtr PropertyTable::Get(const std::string &name) const { - PropertyMap::const_iterator it = props.find(name); - if (it == props.end()) { - // hasn't been parsed yet? - LazyPropertyMap::const_iterator lit = lazyProps.find(name); - if (lit != lazyProps.end()) { - props[name] = ReadTypedProperty(lit->second); - it = props.find(name); - - //ai_assert(it != props.end()); - } - - if (it == props.end()) { - // check property template - return nullptr; - } - } - - return (*it).second; -} - -DirectPropertyMap PropertyTable::GetUnparsedProperties() const { - DirectPropertyMap result; - - // Loop through all the lazy properties (which is all the properties) - for (const LazyPropertyMap::value_type &element : lazyProps) { - // Skip parsed properties - if (props.end() != props.find(element.first)) { - continue; - } - - // Read the element's value. - // Wrap the naked pointer (since the call site is required to acquire ownership) - // std::unique_ptr from C++11 would be preferred both as a wrapper and a return value. - Property *prop = ReadTypedProperty(element.second); - - // Element could not be read. Skip it. - if (!prop) { - continue; - } - - // Add to result - result[element.first] = prop; - } - - return result; -} -} // namespace FBXDocParser diff --git a/modules/fbx/fbx_parser/FBXProperties.h b/modules/fbx/fbx_parser/FBXProperties.h deleted file mode 100644 index bfd27ac94e..0000000000 --- a/modules/fbx/fbx_parser/FBXProperties.h +++ /dev/null @@ -1,212 +0,0 @@ -/*************************************************************************/ -/* FBXProperties.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXProperties.h - * @brief FBX dynamic properties - */ -#ifndef FBX_PROPERTIES_H -#define FBX_PROPERTIES_H - -#include "FBXParser.h" -#include <map> -#include <memory> -#include <string> -#include <vector> - -namespace FBXDocParser { - -// Forward declarations -class Element; - -/** Represents a dynamic property. Type info added by deriving classes, - * see #TypedProperty. - Example: - @verbatim - P: "ShininessExponent", "double", "Number", "",0.5 - @endvebatim -*/ -class Property { -protected: - Property(); - -public: - virtual ~Property(); - -public: - template <typename T> - const T *As() const { - return dynamic_cast<const T *>(this); - } -}; - -template <typename T> -class TypedProperty : public Property { -public: - explicit TypedProperty(const T &value) : - value(value) { - // empty - } - - const T &Value() const { - return value; - } - -private: - T value; -}; - -#define new_Property new Property -typedef Property *PropertyPtr; -typedef std::map<std::string, PropertyPtr> DirectPropertyMap; -typedef std::map<std::string, PropertyPtr> PropertyMap; -typedef std::map<std::string, ElementPtr> LazyPropertyMap; - -/** - * Represents a property table as can be found in the newer FBX files (Properties60, Properties70) - */ -class PropertyTable { -public: - // in-memory property table with no source element - 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() { - return element; - } - - PropertyMap &GetProperties() { - return props; - } - - const LazyPropertyMap &GetLazyProperties() { - return lazyProps; - } - - DirectPropertyMap GetUnparsedProperties() const; - -private: - LazyPropertyMap lazyProps; - mutable PropertyMap props; - ElementPtr element = nullptr; -}; - -// ------------------------------------------------------------------------------------------------ -template <typename T> -inline T PropertyGet(const PropertyTable *in, const std::string &name, const T &defaultValue) { - PropertyPtr prop = in->Get(name); - if (nullptr == prop) { - return defaultValue; - } - - // strong typing, no need to be lenient - const TypedProperty<T> *const tprop = prop->As<TypedProperty<T>>(); - if (nullptr == tprop) { - return defaultValue; - } - - return tprop->Value(); -} - -// ------------------------------------------------------------------------------------------------ -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 (nullptr == in) { - result = false; - return T(); - } - prop = in->Get(name); - if (nullptr == prop) { - result = false; - return T(); - } - } - - // strong typing, no need to be lenient - const TypedProperty<T> *const tprop = prop->As<TypedProperty<T>>(); - if (nullptr == tprop) { - result = false; - return T(); - } - - result = true; - return tprop->Value(); -} -} // namespace FBXDocParser - -#endif // FBX_PROPERTIES_H diff --git a/modules/fbx/fbx_parser/FBXTokenizer.cpp b/modules/fbx/fbx_parser/FBXTokenizer.cpp deleted file mode 100644 index 81c5b128e8..0000000000 --- a/modules/fbx/fbx_parser/FBXTokenizer.cpp +++ /dev/null @@ -1,253 +0,0 @@ -/*************************************************************************/ -/* FBXTokenizer.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXTokenizer.cpp - * @brief Implementation of the FBX broadphase lexer - */ - -// tab width for logging columns -#define ASSIMP_FBX_TAB_WIDTH 4 - -#include "FBXTokenizer.h" -#include "core/string/print_string.h" - -namespace FBXDocParser { - -// ------------------------------------------------------------------------------------------------ -Token::Token(const char *p_sbegin, const char *p_send, TokenType p_type, unsigned int p_line, unsigned int p_column) : - sbegin(p_sbegin), - send(p_send), - type(p_type), - line(p_line), - column(p_column) { -#ifdef DEBUG_ENABLED - contents = std::string(sbegin, static_cast<size_t>(send - sbegin)); -#endif -} - -// ------------------------------------------------------------------------------------------------ -Token::~Token() { -} - -namespace { - -// ------------------------------------------------------------------------------------------------ -void TokenizeError(const std::string &message, unsigned int line, unsigned int column) { - print_error("[FBX-Tokenize]" + String(message.c_str()) + " " + itos(line) + ":" + itos(column)); -} - -// process a potential data token up to 'cur', adding it to 'output_tokens'. -// ------------------------------------------------------------------------------------------------ -void ProcessDataToken(TokenList &output_tokens, const char *&start, const char *&end, - unsigned int line, - unsigned int column, - TokenType type = TokenType_DATA, - bool must_have_token = false) { - if (start && end) { - // sanity check: - // tokens should have no whitespace outside quoted text and [start,end] should - // properly delimit the valid range. - bool in_double_quotes = false; - for (const char *c = start; c != end + 1; ++c) { - if (*c == '\"') { - in_double_quotes = !in_double_quotes; - } - - if (!in_double_quotes && IsSpaceOrNewLine(*c)) { - TokenizeError("unexpected whitespace in token", line, column); - } - } - - if (in_double_quotes) { - TokenizeError("non-terminated double quotes", line, column); - } - - output_tokens.push_back(new_Token(start, end + 1, type, line, column)); - } else if (must_have_token) { - TokenizeError("unexpected character, expected data token", line, column); - } - - start = end = nullptr; -} -} // namespace - -// ------------------------------------------------------------------------------------------------ -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; - - bool comment = false; - bool in_double_quotes = false; - bool pending_data_token = false; - - const char *token_begin = nullptr, *token_end = nullptr; - - // input (starting string), *cur the current string, column += - // modified to fix strlen() and stop buffer overflow - for (size_t x = 0; x < length; x++) { - const char c = input[x]; - const char *cur = &input[x]; - column += (c == '\t' ? ASSIMP_FBX_TAB_WIDTH : 1); - - if (IsLineEnd(c)) { - comment = false; - - column = 0; - ++line; - } - - if (comment) { - continue; - } - - if (in_double_quotes) { - if (c == '\"') { - in_double_quotes = false; - token_end = cur; - - ProcessDataToken(output_tokens, token_begin, token_end, line, column); - pending_data_token = false; - } - continue; - } - - switch (c) { - case '\"': - if (token_begin) { - TokenizeError("unexpected double-quote", line, column); - corrupt = true; - return; - } - token_begin = cur; - in_double_quotes = true; - continue; - - case ';': - ProcessDataToken(output_tokens, token_begin, token_end, line, column); - comment = true; - continue; - - case '{': - ProcessDataToken(output_tokens, token_begin, token_end, line, column); - output_tokens.push_back(new_Token(cur, cur + 1, TokenType_OPEN_BRACKET, line, column)); - continue; - - case '}': - ProcessDataToken(output_tokens, token_begin, token_end, line, column); - output_tokens.push_back(new_Token(cur, cur + 1, TokenType_CLOSE_BRACKET, line, column)); - continue; - - case ',': - if (pending_data_token) { - ProcessDataToken(output_tokens, token_begin, token_end, line, column, TokenType_DATA, true); - } - output_tokens.push_back(new_Token(cur, cur + 1, TokenType_COMMA, line, column)); - continue; - - case ':': - if (pending_data_token) { - ProcessDataToken(output_tokens, token_begin, token_end, line, column, TokenType_KEY, true); - } else { - TokenizeError("unexpected colon", line, column); - } - continue; - } - - if (IsSpaceOrNewLine(c)) { - if (token_begin) { - // peek ahead and check if the next token is a colon in which - // case this counts as KEY token. - TokenType type = TokenType_DATA; - for (const char *peek = cur; *peek && IsSpaceOrNewLine(*peek); ++peek) { - if (*peek == ':') { - type = TokenType_KEY; - cur = peek; - break; - } - } - - ProcessDataToken(output_tokens, token_begin, token_end, line, column, type); - } - - pending_data_token = false; - } else { - token_end = cur; - if (!token_begin) { - token_begin = cur; - } - - pending_data_token = true; - } - } -} -} // namespace FBXDocParser diff --git a/modules/fbx/fbx_parser/FBXTokenizer.h b/modules/fbx/fbx_parser/FBXTokenizer.h deleted file mode 100644 index 184d0fd894..0000000000 --- a/modules/fbx/fbx_parser/FBXTokenizer.h +++ /dev/null @@ -1,203 +0,0 @@ -/*************************************************************************/ -/* FBXTokenizer.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXTokenizer.h - * @brief FBX lexer - */ -#ifndef FBX_TOKENIZER_H -#define FBX_TOKENIZER_H - -#include "FBXParseTools.h" -#include "core/string/ustring.h" -#include <iostream> -#include <memory> -#include <string> -#include <vector> - -namespace FBXDocParser { -/** Rough classification for text FBX tokens used for constructing the - * basic scope hierarchy. */ -enum TokenType { - // { - TokenType_OPEN_BRACKET = 0, - - // } - TokenType_CLOSE_BRACKET, - - // '"blablubb"', '2', '*14' - very general token class, - // further processing happens at a later stage. - TokenType_DATA, - - // - TokenType_BINARY_DATA, - - // , - TokenType_COMMA, - - // blubb: - TokenType_KEY -}; - -/** Represents a single token in a FBX file. Tokens are - * classified by the #TokenType enumerated types. - * - * Offers iterator protocol. Tokens are immutable. */ -class Token { -private: - static const unsigned int BINARY_MARKER = static_cast<unsigned int>(-1); - -public: - /** construct a textual token */ - Token(const char *p_sbegin, const char *p_send, TokenType p_type, unsigned int p_line, unsigned int p_column); - - /** construct a binary token */ - Token(const char *p_sbegin, const char *p_send, TokenType p_type, size_t p_offset); - ~Token(); - -public: - std::string StringContents() const { - return std::string(begin(), end()); - } - - bool IsBinary() const { - return column == BINARY_MARKER; - } - - const char *begin() const { - return sbegin; - } - - const char *end() const { - return send; - } - - TokenType Type() const { - return type; - } - - size_t Offset() const { - return offset; - } - - unsigned int Line() const { - return static_cast<unsigned int>(line); - } - - unsigned int Column() const { - return column; - } - -private: -#ifdef DEBUG_ENABLED - // full string copy for the sole purpose that it nicely appears - // in msvc's debugger window. - std::string contents; -#endif - - const char *sbegin = nullptr; - const char *send = nullptr; - const TokenType type; - - union { - size_t line; - size_t offset; - }; - const unsigned int column = 0; -}; - -// Fixed leak by using shared_ptr for tokens -typedef Token *TokenPtr; -typedef std::vector<TokenPtr> TokenList; - -#define new_Token new Token - -/** Main FBX tokenizer function. Transform input buffer into a list of preprocessed tokens. - * - * Skips over comments and generates line and column numbers. - * - * @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, bool &corrupt); - -/** Tokenizer function for binary FBX files. - * - * Emits a token list suitable for direct parsing. - * - * @param output_tokens Receives a list of all tokens in the input data. - * @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, bool &corrupt); -} // namespace FBXDocParser - -#endif // FBX_TOKENIZER_H diff --git a/modules/fbx/fbx_parser/FBXUtil.cpp b/modules/fbx/fbx_parser/FBXUtil.cpp deleted file mode 100644 index df46bd85c7..0000000000 --- a/modules/fbx/fbx_parser/FBXUtil.cpp +++ /dev/null @@ -1,222 +0,0 @@ -/*************************************************************************/ -/* FBXUtil.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXUtil.cpp - * @brief Implementation of internal FBX utility functions - */ - -#include "FBXUtil.h" -#include "FBXTokenizer.h" -#include <cstring> -#include <string> - -namespace FBXDocParser { -namespace Util { - -// ------------------------------------------------------------------------------------------------ -const char *TokenTypeString(TokenType t) { - switch (t) { - case TokenType_OPEN_BRACKET: - return "TOK_OPEN_BRACKET"; - - case TokenType_CLOSE_BRACKET: - return "TOK_CLOSE_BRACKET"; - - case TokenType_DATA: - return "TOK_DATA"; - - case TokenType_COMMA: - return "TOK_COMMA"; - - case TokenType_KEY: - return "TOK_KEY"; - - case TokenType_BINARY_DATA: - return "TOK_BINARY_DATA"; - } - - //ai_assert(false); - return ""; -} - -// Generated by this formula: T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i; -static const uint8_t base64DecodeTable[128] = { - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 255, 255, 255, - 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, - 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255 -}; - -uint8_t DecodeBase64(char ch) { - const uint8_t idx = static_cast<uint8_t>(ch); - if (idx > 127) { - return 255; - } - return base64DecodeTable[idx]; -} - -size_t ComputeDecodedSizeBase64(const char *in, size_t inLength) { - if (inLength < 2) { - return 0; - } - const size_t equals = size_t(in[inLength - 1] == '=') + size_t(in[inLength - 2] == '='); - const size_t full_length = (inLength * 3) >> 2; // div by 4 - if (full_length < equals) { - return 0; - } - return full_length - equals; -} - -size_t DecodeBase64(const char *in, size_t inLength, uint8_t *out, size_t maxOutLength) { - if (maxOutLength == 0 || inLength < 2) { - return 0; - } - const size_t realLength = inLength - size_t(in[inLength - 1] == '=') - size_t(in[inLength - 2] == '='); - size_t dst_offset = 0; - int val = 0, valb = -8; - for (size_t src_offset = 0; src_offset < realLength; ++src_offset) { - const uint8_t table_value = Util::DecodeBase64(in[src_offset]); - if (table_value == 255) { - return 0; - } - val = (val << 6) + table_value; - valb += 6; - if (valb >= 0) { - out[dst_offset++] = static_cast<uint8_t>((val >> valb) & 0xFF); - valb -= 8; - val &= 0xFFF; - } - } - return dst_offset; -} - -static const char to_base64_string[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -char EncodeBase64(char byte) { - return to_base64_string[(size_t)byte]; -} - -/** Encodes a block of 4 bytes to base64 encoding - * @param bytes Bytes to encode. - * @param out_string String to write encoded values to. - * @param string_pos Position in out_string. - */ -void EncodeByteBlock(const char *bytes, std::string &out_string, size_t string_pos) { - char b0 = (bytes[0] & 0xFC) >> 2; - char b1 = (bytes[0] & 0x03) << 4 | ((bytes[1] & 0xF0) >> 4); - char b2 = (bytes[1] & 0x0F) << 2 | ((bytes[2] & 0xC0) >> 6); - char b3 = (bytes[2] & 0x3F); - - out_string[string_pos + 0] = EncodeBase64(b0); - out_string[string_pos + 1] = EncodeBase64(b1); - out_string[string_pos + 2] = EncodeBase64(b2); - out_string[string_pos + 3] = EncodeBase64(b3); -} - -std::string EncodeBase64(const char *data, size_t length) { - // calculate extra bytes needed to get a multiple of 3 - size_t extraBytes = 3 - length % 3; - - // number of base64 bytes - size_t encodedBytes = 4 * (length + extraBytes) / 3; - - std::string encoded_string(encodedBytes, '='); - - // read blocks of 3 bytes - for (size_t ib3 = 0; ib3 < length / 3; ib3++) { - const size_t iByte = ib3 * 3; - const size_t iEncodedByte = ib3 * 4; - const char *currData = &data[iByte]; - - EncodeByteBlock(currData, encoded_string, iEncodedByte); - } - - // if size of data is not a multiple of 3, also encode the final bytes (and add zeros where needed) - if (extraBytes > 0) { - char finalBytes[4] = { 0, 0, 0, 0 }; - memcpy(&finalBytes[0], &data[length - length % 3], length % 3); - - const size_t iEncodedByte = encodedBytes - 4; - EncodeByteBlock(&finalBytes[0], encoded_string, iEncodedByte); - - // add '=' at the end - for (size_t i = 0; i < 4 * extraBytes / 3; i++) { - encoded_string[encodedBytes - i - 1] = '='; - } - } - return encoded_string; -} -} // namespace Util -} // namespace FBXDocParser diff --git a/modules/fbx/fbx_parser/FBXUtil.h b/modules/fbx/fbx_parser/FBXUtil.h deleted file mode 100644 index dab2a4201e..0000000000 --- a/modules/fbx/fbx_parser/FBXUtil.h +++ /dev/null @@ -1,122 +0,0 @@ -/*************************************************************************/ -/* FBXUtil.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2019, assimp team - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file FBXUtil.h - * @brief FBX utility functions for internal use - */ -#ifndef FBX_UTIL_H -#define FBX_UTIL_H - -#include "FBXTokenizer.h" -#include <stdint.h> - -namespace FBXDocParser { - -namespace Util { - -/** Get a string representation for a #TokenType. */ -const char *TokenTypeString(TokenType t); - -/** Decode a single Base64-encoded character. - * - * @param ch Character to decode (from base64 to binary). - * @return decoded byte value*/ -uint8_t DecodeBase64(char ch); - -/** Compute decoded size of a Base64-encoded string - * - * @param in Characters to decode. - * @param inLength Number of characters to decode. - * @return size of the decoded data (number of bytes)*/ -size_t ComputeDecodedSizeBase64(const char *in, size_t inLength); - -/** Decode a Base64-encoded string - * - * @param in Characters to decode. - * @param inLength Number of characters to decode. - * @param out Pointer where we will store the decoded data. - * @param maxOutLength Size of output buffer. - * @return size of the decoded data (number of bytes)*/ -size_t DecodeBase64(const char *in, size_t inLength, uint8_t *out, size_t maxOutLength); - -char EncodeBase64(char byte); - -/** Encode bytes in base64-encoding - * - * @param data Binary data to encode. - * @param inLength Number of bytes to encode. - * @return base64-encoded string*/ -std::string EncodeBase64(const char *data, size_t length); -} // namespace Util -} // namespace FBXDocParser - -#endif // FBX_UTIL_H diff --git a/modules/fbx/fbx_parser/LICENSE b/modules/fbx/fbx_parser/LICENSE deleted file mode 100644 index b42fc6efe6..0000000000 --- a/modules/fbx/fbx_parser/LICENSE +++ /dev/null @@ -1,39 +0,0 @@ -The files in this folder were originally from ASSIMP, but have been heavily modified to fix bugs and match coding -conventions of the Godot Engine project. We have kept a copy of the applicable licenses in the folder as required by -the license. - -Open Asset Import Library (assimp) - -Copyright (c) 2006-2020, assimp team -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - diff --git a/modules/fbx/register_types.cpp b/modules/fbx/register_types.cpp deleted file mode 100644 index d5e520a060..0000000000 --- a/modules/fbx/register_types.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/*************************************************************************/ -/* register_types.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "register_types.h" - -#include "editor/editor_node.h" -#include "editor_scene_importer_fbx.h" - -#ifdef TOOLS_ENABLED -static void _editor_init() { - Ref<EditorSceneFormatImporterFBX> import_fbx; - import_fbx.instantiate(); - ResourceImporterScene::get_singleton()->add_importer(import_fbx); -} -#endif - -void register_fbx_types() { -#ifdef TOOLS_ENABLED - ClassDB::APIType prev_api = ClassDB::get_current_api(); - ClassDB::set_current_api(ClassDB::API_EDITOR); - - GDREGISTER_CLASS(EditorSceneFormatImporterFBX); - - ClassDB::set_current_api(prev_api); - - EditorNode::add_init_callback(_editor_init); -#endif -} - -void unregister_fbx_types() { -} diff --git a/modules/fbx/register_types.h b/modules/fbx/register_types.h deleted file mode 100644 index e5741afd72..0000000000 --- a/modules/fbx/register_types.h +++ /dev/null @@ -1,37 +0,0 @@ -/*************************************************************************/ -/* register_types.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef FBX_REGISTER_TYPES_H -#define FBX_REGISTER_TYPES_H - -void register_fbx_types(); -void unregister_fbx_types(); - -#endif // FBX_REGISTER_TYPES_H diff --git a/modules/fbx/tools/import_utils.cpp b/modules/fbx/tools/import_utils.cpp deleted file mode 100644 index bb95d120af..0000000000 --- a/modules/fbx/tools/import_utils.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/*************************************************************************/ -/* import_utils.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "import_utils.h" - -Vector3 ImportUtils::deg2rad(const Vector3 &p_rotation) { - return p_rotation / 180.0 * Math_PI; -} - -Vector3 ImportUtils::rad2deg(const Vector3 &p_rotation) { - return p_rotation / Math_PI * 180.0; -} - -Basis ImportUtils::EulerToBasis(FBXDocParser::Model::RotOrder mode, const Vector3 &p_rotation) { - Basis ret; - - // FBX is using intrinsic euler, we can convert intrinsic to extrinsic (the one used in godot - // by simply invert its order: https://www.cs.utexas.edu/~theshark/courses/cs354/lectures/cs354-14.pdf - switch (mode) { - case FBXDocParser::Model::RotOrder_EulerXYZ: - ret.set_euler(p_rotation, Basis::EULER_ORDER_XYZ); - break; - - case FBXDocParser::Model::RotOrder_EulerXZY: - ret.set_euler(p_rotation, Basis::EULER_ORDER_XZY); - break; - - case FBXDocParser::Model::RotOrder_EulerYZX: - ret.set_euler(p_rotation, Basis::EULER_ORDER_YZX); - break; - - case FBXDocParser::Model::RotOrder_EulerYXZ: - ret.set_euler(p_rotation, Basis::EULER_ORDER_YXZ); - break; - - case FBXDocParser::Model::RotOrder_EulerZXY: - ret.set_euler(p_rotation, Basis::EULER_ORDER_ZXY); - break; - - case FBXDocParser::Model::RotOrder_EulerZYX: - ret.set_euler(p_rotation, Basis::EULER_ORDER_ZYX); - break; - - case FBXDocParser::Model::RotOrder_SphericXYZ: - // TODO do this. - break; - - default: - // If you land here, Please integrate all enums. - CRASH_NOW_MSG("This is not unreachable."); - } - - return ret; -} - -Quaternion ImportUtils::EulerToQuaternion(FBXDocParser::Model::RotOrder mode, const Vector3 &p_rotation) { - return ImportUtils::EulerToBasis(mode, p_rotation); -} - -Vector3 ImportUtils::BasisToEuler(FBXDocParser::Model::RotOrder mode, const Basis &p_rotation) { - // FBX is using intrinsic euler, we can convert intrinsic to extrinsic (the one used in godot - // by simply invert its order: https://www.cs.utexas.edu/~theshark/courses/cs354/lectures/cs354-14.pdf - switch (mode) { - case FBXDocParser::Model::RotOrder_EulerXYZ: - return p_rotation.get_euler(Basis::EULER_ORDER_XYZ); - - case FBXDocParser::Model::RotOrder_EulerXZY: - return p_rotation.get_euler(Basis::EULER_ORDER_XZY); - - case FBXDocParser::Model::RotOrder_EulerYZX: - return p_rotation.get_euler(Basis::EULER_ORDER_YZX); - - case FBXDocParser::Model::RotOrder_EulerYXZ: - return p_rotation.get_euler(Basis::EULER_ORDER_YXZ); - - case FBXDocParser::Model::RotOrder_EulerZXY: - return p_rotation.get_euler(Basis::EULER_ORDER_ZXY); - - case FBXDocParser::Model::RotOrder_EulerZYX: - return p_rotation.get_euler(Basis::EULER_ORDER_ZYX); - - case FBXDocParser::Model::RotOrder_SphericXYZ: - // TODO - return Vector3(); - - default: - // If you land here, Please integrate all enums. - CRASH_NOW_MSG("This is not unreachable."); - return Vector3(); - } -} - -Vector3 ImportUtils::QuaternionToEuler(FBXDocParser::Model::RotOrder mode, const Quaternion &p_rotation) { - return BasisToEuler(mode, p_rotation); -} - -Transform3D get_unscaled_transform(const Transform3D &p_initial, real_t p_scale) { - Transform3D unscaled = Transform3D(p_initial.basis, p_initial.origin * p_scale); - ERR_FAIL_COND_V_MSG(unscaled.basis.determinant() == 0, Transform3D(), "det is zero unscaled?"); - return unscaled; -} - -Vector3 get_poly_normal(const std::vector<Vector3> &p_vertices) { - ERR_FAIL_COND_V_MSG(p_vertices.size() < 3, Vector3(0, 0, 0), "At least 3 vertices are necessary"); - // Using long double to make sure that normal is computed for even really tiny objects. - typedef long double ldouble; - ldouble x = 0.0; - ldouble y = 0.0; - ldouble z = 0.0; - for (size_t i = 0; i < p_vertices.size(); i += 1) { - const Vector3 current = p_vertices[i]; - const Vector3 next = p_vertices[(i + 1) % p_vertices.size()]; - x += (ldouble(current.y) - ldouble(next.y)) * (ldouble(current.z) + ldouble(next.z)); - y += (ldouble(current.z) - ldouble(next.z)) * (ldouble(current.x) + ldouble(next.x)); - z += (ldouble(current.x) - ldouble(next.x)) * (ldouble(current.y) + ldouble(next.y)); - } - const ldouble l2 = x * x + y * y + z * z; - if (l2 == 0.0) { - return (p_vertices[0] - p_vertices[1]).normalized().cross((p_vertices[0] - p_vertices[2]).normalized()).normalized(); - } else { - const double l = Math::sqrt(double(l2)); - return Vector3(x / l, y / l, z / l); - } -} diff --git a/modules/fbx/tools/import_utils.h b/modules/fbx/tools/import_utils.h deleted file mode 100644 index 88c71fb87e..0000000000 --- a/modules/fbx/tools/import_utils.h +++ /dev/null @@ -1,400 +0,0 @@ -/*************************************************************************/ -/* import_utils.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef IMPORT_UTILS_FBX_IMPORTER_H -#define IMPORT_UTILS_FBX_IMPORTER_H - -#include "core/io/image_loader.h" - -#include "data/import_state.h" -#include "fbx_parser/FBXDocument.h" - -#include <string> - -#define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000LL - -/** - * Import Utils - * Conversion tools / glue code to convert from FBX to Godot - */ -class ImportUtils { -public: - /// Convert a vector from degrees to radians. - static Vector3 deg2rad(const Vector3 &p_rotation); - - /// Convert a vector from radians to degrees. - static Vector3 rad2deg(const Vector3 &p_rotation); - - /// Converts rotation order vector (in rad) to quaternion. - static Basis EulerToBasis(FBXDocParser::Model::RotOrder mode, const Vector3 &p_rotation); - - /// Converts rotation order vector (in rad) to quaternion. - static Quaternion EulerToQuaternion(FBXDocParser::Model::RotOrder mode, const Vector3 &p_rotation); - - /// Converts basis into rotation order vector (in rad). - static Vector3 BasisToEuler(FBXDocParser::Model::RotOrder mode, const Basis &p_rotation); - - /// Converts quaternion into rotation order vector (in rad). - static Vector3 QuaternionToEuler(FBXDocParser::Model::RotOrder mode, const Quaternion &p_rotation); - - static void debug_xform(String name, const Transform3D &t) { - print_verbose(name + " " + t.origin + " rotation: " + (t.basis.get_euler() * (180 / Math_PI))); - } - - static String FBXNodeToName(const std::string &name) { - // strip Model:: prefix, avoiding ambiguities (i.e. don't strip if - // this causes ambiguities, well possible between empty identifiers, - // such as "Model::" and ""). Make sure the behaviour is consistent - // across multiple calls to FixNodeName(). - - // We must remove this from the name - // Some bones have this - // SubDeformer:: - // Meshes, Joints have this, some other IK elements too. - // Model:: - - String node_name = String(name.c_str()); - - if (node_name.substr(0, 7) == "Model::") { - node_name = node_name.substr(7, node_name.length() - 7); - return node_name.replace(":", ""); - } - - if (node_name.substr(0, 13) == "SubDeformer::") { - node_name = node_name.substr(13, node_name.length() - 13); - return node_name.replace(":", ""); - } - - if (node_name.substr(0, 11) == "AnimStack::") { - node_name = node_name.substr(11, node_name.length() - 11); - return node_name.replace(":", ""); - } - - if (node_name.substr(0, 15) == "AnimCurveNode::") { - node_name = node_name.substr(15, node_name.length() - 15); - return node_name.replace(":", ""); - } - - if (node_name.substr(0, 11) == "AnimCurve::") { - node_name = node_name.substr(11, node_name.length() - 11); - return node_name.replace(":", ""); - } - - if (node_name.substr(0, 10) == "Geometry::") { - node_name = node_name.substr(10, node_name.length() - 10); - return node_name.replace(":", ""); - } - - if (node_name.substr(0, 10) == "Material::") { - node_name = node_name.substr(10, node_name.length() - 10); - return node_name.replace(":", ""); - } - - if (node_name.substr(0, 9) == "Texture::") { - node_name = node_name.substr(9, node_name.length() - 9); - return node_name.replace(":", ""); - } - - return node_name.replace(":", ""); - } - - static std::string FBXAnimMeshName(const std::string &name) { - if (name.length()) { - size_t indexOf = name.find_first_of("::"); - if (indexOf != std::string::npos && indexOf < name.size() - 2) { - return name.substr(indexOf + 2); - } - } - return name.length() ? name : "AnimMesh"; - } - - static Vector3 safe_import_vector3(const Vector3 &p_vec) { - Vector3 vector = p_vec; - if (Math::is_zero_approx(vector.x)) { - vector.x = 0; - } - - if (Math::is_zero_approx(vector.y)) { - vector.y = 0; - } - - if (Math::is_zero_approx(vector.z)) { - vector.z = 0; - } - return vector; - } - - static void debug_xform(String name, const Basis &t) { - //print_verbose(name + " rotation: " + (t.get_euler() * (180 / Math_PI))); - } - - static Vector3 FixAxisConversions(Vector3 input) { - return Vector3(input.x, input.y, input.z); - } - - static void AlignMeshAxes(std::vector<Vector3> &vertex_data) { - for (size_t x = 0; x < vertex_data.size(); x++) { - vertex_data[x] = FixAxisConversions(vertex_data[x]); - } - } - - struct AssetImportFbx { - enum ETimeMode { - TIME_MODE_DEFAULT = 0, - TIME_MODE_120 = 1, - TIME_MODE_100 = 2, - TIME_MODE_60 = 3, - TIME_MODE_50 = 4, - TIME_MODE_48 = 5, - TIME_MODE_30 = 6, - TIME_MODE_30_DROP = 7, - TIME_MODE_NTSC_DROP_FRAME = 8, - TIME_MODE_NTSC_FULL_FRAME = 9, - TIME_MODE_PAL = 10, - TIME_MODE_CINEMA = 11, - TIME_MODE_1000 = 12, - TIME_MODE_CINEMA_ND = 13, - TIME_MODE_CUSTOM = 14, - TIME_MODE_TIME_MODE_COUNT = 15 - }; - enum UpAxis { - UP_VECTOR_AXIS_X = 1, - UP_VECTOR_AXIS_Y = 2, - UP_VECTOR_AXIS_Z = 3 - }; - enum FrontAxis { - FRONT_PARITY_EVEN = 1, - FRONT_PARITY_ODD = 2, - }; - - enum CoordAxis { - COORD_RIGHT = 0, - COORD_LEFT = 1 - }; - }; - - /** Get fbx fps for time mode meta data - */ - static float get_fbx_fps(int32_t time_mode) { - switch (time_mode) { - case AssetImportFbx::TIME_MODE_DEFAULT: - return 24; - case AssetImportFbx::TIME_MODE_120: - return 120; - case AssetImportFbx::TIME_MODE_100: - return 100; - case AssetImportFbx::TIME_MODE_60: - return 60; - case AssetImportFbx::TIME_MODE_50: - return 50; - case AssetImportFbx::TIME_MODE_48: - return 48; - case AssetImportFbx::TIME_MODE_30: - return 30; - case AssetImportFbx::TIME_MODE_30_DROP: - return 30; - case AssetImportFbx::TIME_MODE_NTSC_DROP_FRAME: - return 29.9700262f; - case AssetImportFbx::TIME_MODE_NTSC_FULL_FRAME: - return 29.9700262f; - case AssetImportFbx::TIME_MODE_PAL: - return 25; - case AssetImportFbx::TIME_MODE_CINEMA: - return 24; - case AssetImportFbx::TIME_MODE_1000: - return 1000; - case AssetImportFbx::TIME_MODE_CINEMA_ND: - return 23.976f; - case AssetImportFbx::TIME_MODE_CUSTOM: - return -1; - } - return 0; - } - - static float get_fbx_fps(const FBXDocParser::FileGlobalSettings *FBXSettings) { - int time_mode = FBXSettings->TimeMode(); - - // get the animation FPS - float frames_per_second = get_fbx_fps(time_mode); - - // handle animation custom FPS time. - if (time_mode == ImportUtils::AssetImportFbx::TIME_MODE_CUSTOM) { - print_verbose("FBX Animation has custom FPS setting"); - frames_per_second = FBXSettings->CustomFrameRate(); - - // not our problem this is the modeller, we can print as an error so they can fix the source. - if (frames_per_second == 0) { - print_error("Custom animation time in file is set to 0 value, animation won't play, please edit your file to correct the FPS value"); - } - } - return frames_per_second; - } - - /** - * Find hardcoded textures from assimp which could be in many different directories - */ - - /** - * set_texture_mapping_mode - * Helper to check the mapping mode of the texture (repeat, clamp and mirror) - */ - // static void set_texture_mapping_mode(aiTextureMapMode *map_mode, Ref<ImageTexture> texture) { - // ERR_FAIL_COND(texture.is_null()); - // ERR_FAIL_COND(map_mode == nullptr); - // aiTextureMapMode tex_mode = map_mode[0]; - - // int32_t flags = Texture::FLAGS_DEFAULT; - // if (tex_mode == aiTextureMapMode_Wrap) { - // //Default - // } else if (tex_mode == aiTextureMapMode_Clamp) { - // flags = flags & ~Texture::FLAG_REPEAT; - // } else if (tex_mode == aiTextureMapMode_Mirror) { - // flags = flags | Texture::FLAG_MIRRORED_REPEAT; - // } - // texture->set_flags(flags); - // } - - /** - * Load or load from cache image :) - * We need to upgrade this in the later version :) should not be hard - */ - //static Ref<Image> load_image(ImportState &state, const aiScene *p_scene, String p_path){ - // Map<String, Ref<Image> >::Element *match = state.path_to_image_cache.find(p_path); - - // // if our cache contains this image then don't bother - // if (match) { - // return match->get(); - // } - - // Vector<String> split_path = p_path.get_basename().split("*"); - // if (split_path.size() == 2) { - // size_t texture_idx = split_path[1].to_int(); - // ERR_FAIL_COND_V(texture_idx >= p_scene->mNumTextures, Ref<Image>()); - // aiTexture *tex = p_scene->mTextures[texture_idx]; - // String filename = AssimpUtils::get_raw_string_from_assimp(tex->mFilename); - // filename = filename.get_file(); - // print_verbose("Open Asset Import: Loading embedded texture " + filename); - // if (tex->mHeight == 0) { - // if (tex->CheckFormat("png")) { - // Ref<Image> img = Image::_png_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth); - // ERR_FAIL_COND_V(img.is_null(), Ref<Image>()); - // state.path_to_image_cache.insert(p_path, img); - // return img; - // } else if (tex->CheckFormat("jpg")) { - // Ref<Image> img = Image::_jpg_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth); - // ERR_FAIL_COND_V(img.is_null(), Ref<Image>()); - // state.path_to_image_cache.insert(p_path, img); - // return img; - // } else if (tex->CheckFormat("dds")) { - // ERR_FAIL_COND_V_MSG(true, Ref<Image>(), "Open Asset Import: Embedded dds not implemented"); - // } - // } else { - // Ref<Image> img; - // img.instantiate(); - // PoolByteArray arr; - // uint32_t size = tex->mWidth * tex->mHeight; - // arr.resize(size); - // memcpy(arr.write().ptr(), tex->pcData, size); - // ERR_FAIL_COND_V(arr.size() % 4 != 0, Ref<Image>()); - // //ARGB8888 to RGBA8888 - // for (int32_t i = 0; i < arr.size() / 4; i++) { - // arr.write().ptr()[(4 * i) + 3] = arr[(4 * i) + 0]; - // arr.write().ptr()[(4 * i) + 0] = arr[(4 * i) + 1]; - // arr.write().ptr()[(4 * i) + 1] = arr[(4 * i) + 2]; - // arr.write().ptr()[(4 * i) + 2] = arr[(4 * i) + 3]; - // } - // img->create(tex->mWidth, tex->mHeight, true, Image::FORMAT_RGBA8, arr); - // ERR_FAIL_COND_V(img.is_null(), Ref<Image>()); - // state.path_to_image_cache.insert(p_path, img); - // return img; - // } - // return Ref<Image>(); - // } else { - // Ref<Texture> texture = ResourceLoader::load(p_path); - // ERR_FAIL_COND_V(texture.is_null(), Ref<Image>()); - // Ref<Image> image = texture->get_image(); - // ERR_FAIL_COND_V(image.is_null(), Ref<Image>()); - // state.path_to_image_cache.insert(p_path, image); - // return image; - // } - - // return Ref<Image>(); - //} - - // /* create texture from assimp data, if found in path */ - // static bool CreateAssimpTexture( - // AssimpImporter::ImportState &state, - // aiString texture_path, - // String &filename, - // String &path, - // AssimpImageData &image_state) { - // filename = get_raw_string_from_assimp(texture_path); - // path = state.path.get_base_dir().plus_file(filename.replace("\\", "/")); - // bool found = false; - // find_texture_path(state.path, path, found); - // if (found) { - // image_state.raw_image = AssimpUtils::load_image(state, state.assimp_scene, path); - // if (image_state.raw_image.is_valid()) { - // image_state.texture.instantiate(); - // image_state.texture->create_from_image(image_state.raw_image); - // image_state.texture->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY); - // return true; - // } - // } - - // return false; - // } - // /** GetAssimpTexture - // * Designed to retrieve textures for you - // */ - // static bool GetAssimpTexture( - // AssimpImporter::ImportState &state, - // aiMaterial *ai_material, - // aiTextureType texture_type, - // String &filename, - // String &path, - // AssimpImageData &image_state) { - // aiString ai_filename = aiString(); - // if (AI_SUCCESS == ai_material->GetTexture(texture_type, 0, &ai_filename, nullptr, nullptr, nullptr, nullptr, image_state.map_mode)) { - // return CreateAssimpTexture(state, ai_filename, filename, path, image_state); - // } - - // return false; - // } -}; - -// Apply the transforms so the basis will have scale 1. -Transform3D get_unscaled_transform(const Transform3D &p_initial, real_t p_scale); - -/// Uses the Newell's method to compute any polygon normal. -/// The polygon must be at least size of 3 or bigger. -Vector3 get_poly_normal(const std::vector<Vector3> &p_vertices); - -#endif // IMPORT_UTILS_FBX_IMPORTER_H diff --git a/modules/fbx/tools/validation_tools.cpp b/modules/fbx/tools/validation_tools.cpp deleted file mode 100644 index 9dbd8bf544..0000000000 --- a/modules/fbx/tools/validation_tools.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/*************************************************************************/ -/* validation_tools.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "validation_tools.h" - -#ifdef TOOLS_ENABLED - -#include "core/string/print_string.h" -#include "core/string/ustring.h" - -ValidationTracker::Entries *ValidationTracker::entries_singleton = memnew(ValidationTracker::Entries); - -// for printing our CSV to dump validation problems of files -// later we can make some agnostic tooling for this but this is fine for the time being. -void ValidationTracker::Entries::add_validation_error(String asset_path, String message) { - print_error(message); - // note: implementation is static - validation_entries[asset_path].push_back(message); -} - -#endif // TOOLS_ENABLED diff --git a/modules/fbx/tools/validation_tools.h b/modules/fbx/tools/validation_tools.h deleted file mode 100644 index 12d644ee94..0000000000 --- a/modules/fbx/tools/validation_tools.h +++ /dev/null @@ -1,92 +0,0 @@ -/*************************************************************************/ -/* validation_tools.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef FBX_VALIDATION_TOOLS_H -#define FBX_VALIDATION_TOOLS_H - -#ifdef TOOLS_ENABLED - -#include "core/io/file_access.h" -#include "core/string/print_string.h" -#include "core/templates/local_vector.h" -#include "core/templates/map.h" - -class ValidationTracker { -protected: - struct Entries { - Map<String, LocalVector<String>> validation_entries = Map<String, LocalVector<String>>(); - - // for printing our CSV to dump validation problems of files - // later we can make some agnostic tooling for this but this is fine for the time being. - void add_validation_error(String asset_path, String message); - void print_to_csv() { - print_verbose("Exporting assset validation log please wait"); - String massive_log_file; - - String csv_header = "file_path, error message, extra data\n"; - massive_log_file += csv_header; - - for (const KeyValue<String, LocalVector<String>> &element : validation_entries) { - for (unsigned int x = 0; x < element.value.size(); x++) { - const String &line_entry = element.key + ", " + element.value[x].c_escape() + "\n"; - massive_log_file += line_entry; - } - } - - String path = "asset_validation_errors.csv"; - Error err; - FileAccess *file = FileAccess::open(path, FileAccess::WRITE, &err); - if (!file || err) { - if (file) { - memdelete(file); - } - print_error("ValidationTracker Error - failed to create file - path: %s\n" + path); - return; - } - - file->store_string(massive_log_file); - if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) { - print_error("ValidationTracker Error - failed to write to file - path: %s\n" + path); - } - file->close(); - memdelete(file); - } - }; - // asset path, error messages - static Entries *entries_singleton; - -public: - static Entries *get_singleton() { - return entries_singleton; - } -}; - -#endif // TOOLS_ENABLED -#endif // FBX_VALIDATION_TOOLS_H |