You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-12 13:20:55 +00:00
Port FBX module from commit 68013d2393
Ports FBX module from 3.2 branch to 4.0 This is the only time the plugin will be updated from 3.2 and marks the final time we do this, from now on we will backport FBX to 3.2 with fixes. Changelog: - fixed crash importing files with buggy format (because of bad newlines in ASCII data, this is yet to be fixed fully) - fixed const correctness with C++/C version change - rewrote material handling to be simpler and better - ports from 3.2 to 4.0 the fbx importer
This commit is contained in:
46
modules/fbx/data/fbx_anim_container.h
Normal file
46
modules/fbx/data/fbx_anim_container.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*************************************************************************/
|
||||
/* fbx_anim_container.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 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
|
||||
56
modules/fbx/data/fbx_bone.cpp
Normal file
56
modules/fbx/data/fbx_bone.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
/*************************************************************************/
|
||||
/* fbx_bone.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 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;
|
||||
}
|
||||
90
modules/fbx/data/fbx_bone.h
Normal file
90
modules/fbx/data/fbx_bone.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/*************************************************************************/
|
||||
/* fbx_bone.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 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 Reference {
|
||||
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
|
||||
464
modules/fbx/data/fbx_material.cpp
Normal file
464
modules/fbx/data/fbx_material.cpp
Normal file
@@ -0,0 +1,464 @@
|
||||
/*************************************************************************/
|
||||
/* fbx_material.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 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"
|
||||
#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.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) {
|
||||
_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) {
|
||||
_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.instance();
|
||||
|
||||
// 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.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.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.instance(); // 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.instance();
|
||||
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->Props()->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->Props();
|
||||
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.instance();
|
||||
}
|
||||
|
||||
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_equal_approx(real_value->Value(), 0.0f)) {
|
||||
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_equal_approx(real_value->Value(), 0.0f)) {
|
||||
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;
|
||||
}
|
||||
286
modules/fbx/data/fbx_material.h
Normal file
286
modules/fbx/data/fbx_material.h
Normal file
@@ -0,0 +1,286 @@
|
||||
/*************************************************************************/
|
||||
/* fbx_material.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 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/reference.h"
|
||||
#include "core/string/ustring.h"
|
||||
|
||||
struct FBXMaterial : public Reference {
|
||||
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 },
|
||||
|
||||
/* Metallic */
|
||||
{ "Shininess", PROPERTY_DESC_METALLIC },
|
||||
{ "Reflectivity", PROPERTY_DESC_METALLIC },
|
||||
{ "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 */
|
||||
{ "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 : Reference {
|
||||
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
|
||||
1461
modules/fbx/data/fbx_mesh_data.cpp
Normal file
1461
modules/fbx/data/fbx_mesh_data.cpp
Normal file
File diff suppressed because it is too large
Load Diff
183
modules/fbx/data/fbx_mesh_data.h
Normal file
183
modules/fbx/data/fbx_mesh_data.h
Normal file
@@ -0,0 +1,183 @@
|
||||
/*************************************************************************/
|
||||
/* fbx_mesh_data.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 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 "editor/import/resource_importer_scene.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;
|
||||
|
||||
struct VertexWeightMapping {
|
||||
Vector<real_t> 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 : Reference {
|
||||
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;
|
||||
|
||||
EditorSceneImporterMeshNode *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;
|
||||
EditorSceneImporterMeshNode *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(Ref<SurfaceTool> st, Vector<int> p_polygon_vertex, Vector<int> p_surface_vertex_map, 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 poligon 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
|
||||
63
modules/fbx/data/fbx_node.h
Normal file
63
modules/fbx/data/fbx_node.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*************************************************************************/
|
||||
/* fbx_node.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 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 : Reference, 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
|
||||
123
modules/fbx/data/fbx_skeleton.cpp
Normal file
123
modules/fbx/data/fbx_skeleton.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
/*************************************************************************/
|
||||
/* fbx_skeleton.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 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; 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 (Map<int, Ref<FBXBone>>::Element *bone_element = bone_map.front(); bone_element; bone_element = bone_element->next()) {
|
||||
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));
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
53
modules/fbx/data/fbx_skeleton.h
Normal file
53
modules/fbx/data/fbx_skeleton.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/*************************************************************************/
|
||||
/* fbx_skeleton.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 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/reference.h"
|
||||
#include "scene/3d/skeleton_3d.h"
|
||||
|
||||
struct FBXNode;
|
||||
struct ImportState;
|
||||
struct FBXBone;
|
||||
|
||||
struct FBXSkeleton : Reference {
|
||||
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
|
||||
112
modules/fbx/data/import_state.h
Normal file
112
modules/fbx/data/import_state.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/*************************************************************************/
|
||||
/* import_state.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 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/core_bind.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;
|
||||
|
||||
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
|
||||
52
modules/fbx/data/model_abstraction.h
Normal file
52
modules/fbx/data/model_abstraction.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*************************************************************************/
|
||||
/* model_abstraction.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 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
|
||||
294
modules/fbx/data/pivot_transform.cpp
Normal file
294
modules/fbx/data/pivot_transform.cpp
Normal file
@@ -0,0 +1,294 @@
|
||||
/*************************************************************************/
|
||||
/* pivot_transform.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 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->Props();
|
||||
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;
|
||||
}
|
||||
const Vector3 &GeometricScaling = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "GeometricScaling", ok));
|
||||
if (ok) {
|
||||
geometric_scaling = GeometricScaling;
|
||||
} else {
|
||||
geometric_scaling = Vector3(0, 0, 0);
|
||||
}
|
||||
|
||||
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 = Quat();
|
||||
}
|
||||
|
||||
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 != Quat()) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Transform PivotTransform::ComputeLocalTransform(Vector3 p_translation, Quat p_rotation, Vector3 p_scaling) const {
|
||||
Transform 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
|
||||
Transform Rpre = Transform(pre_rotation);
|
||||
Transform R = Transform(p_rotation);
|
||||
Transform Rpost = Transform(post_rotation);
|
||||
|
||||
return T * Roff * Rp * Rpre * R * Rpost.affine_inverse() * Rp.affine_inverse() * Soff * Sp * S * Sp.affine_inverse();
|
||||
}
|
||||
|
||||
Transform PivotTransform::ComputeGlobalTransform(Transform t) const {
|
||||
Vector3 pos = t.origin;
|
||||
Vector3 scale = t.basis.get_scale();
|
||||
Quat rot = t.basis.get_rotation_quat();
|
||||
return ComputeGlobalTransform(pos, rot, scale);
|
||||
}
|
||||
|
||||
Transform PivotTransform::ComputeLocalTransform(Transform t) const {
|
||||
Vector3 pos = t.origin;
|
||||
Vector3 scale = t.basis.get_scale();
|
||||
Quat rot = t.basis.get_rotation_quat();
|
||||
return ComputeLocalTransform(pos, rot, scale);
|
||||
}
|
||||
|
||||
Transform PivotTransform::ComputeGlobalTransform(Vector3 p_translation, Quat p_rotation, Vector3 p_scaling) const {
|
||||
Transform 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
|
||||
Transform Rpre = Transform(pre_rotation);
|
||||
Transform R = Transform(p_rotation);
|
||||
Transform Rpost = Transform(post_rotation);
|
||||
|
||||
Transform parent_global_xform;
|
||||
Transform parent_local_scaling_m;
|
||||
|
||||
if (parent_transform.is_valid()) {
|
||||
parent_global_xform = parent_transform->GlobalTransform;
|
||||
parent_local_scaling_m = parent_transform->Local_Scaling_Matrix;
|
||||
}
|
||||
|
||||
Transform local_rotation_m, parent_global_rotation_m;
|
||||
Quat parent_global_rotation = parent_global_xform.basis.get_rotation_quat();
|
||||
parent_global_rotation_m.basis.set_quat(parent_global_rotation);
|
||||
local_rotation_m = Rpre * R * Rpost;
|
||||
|
||||
//Basis parent_global_rotation = Basis(parent_global_xform.get_basis().get_rotation_quat().normalized());
|
||||
|
||||
Transform 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.
|
||||
Transform 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) {
|
||||
Transform 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;
|
||||
}
|
||||
Transform local_transform = T * Roff * Rp * Rpre * R * Rpost.affine_inverse() * Rp.affine_inverse() * Soff * Sp * S * Sp.affine_inverse();
|
||||
//Transform local_translation_pivoted = Transform(Basis(), LocalTransform.origin);
|
||||
|
||||
// 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() {
|
||||
Transform 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
|
||||
Transform Rpre = Transform(pre_rotation);
|
||||
Transform R = Transform(rotation);
|
||||
Transform Rpost = Transform(post_rotation);
|
||||
|
||||
Transform parent_global_xform;
|
||||
Transform parent_local_scaling_m;
|
||||
|
||||
if (parent_transform.is_valid()) {
|
||||
parent_global_xform = parent_transform->GlobalTransform;
|
||||
parent_local_scaling_m = parent_transform->Local_Scaling_Matrix;
|
||||
}
|
||||
|
||||
Transform local_rotation_m, parent_global_rotation_m;
|
||||
Quat parent_global_rotation = parent_global_xform.basis.get_rotation_quat();
|
||||
parent_global_rotation_m.basis.set_quat(parent_global_rotation);
|
||||
local_rotation_m = Rpre * R * Rpost;
|
||||
|
||||
//Basis parent_global_rotation = Basis(parent_global_xform.get_basis().get_rotation_quat().normalized());
|
||||
|
||||
Transform 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.
|
||||
Transform 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) {
|
||||
Transform 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 = Transform();
|
||||
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");
|
||||
|
||||
Transform local_translation_pivoted = Transform(Basis(), LocalTransform.origin);
|
||||
GlobalTransform = Transform();
|
||||
//GlobalTransform = parent_global_xform * LocalTransform;
|
||||
Transform global_origin = Transform(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);
|
||||
computed_global_xform = true;
|
||||
}
|
||||
115
modules/fbx/data/pivot_transform.h
Normal file
115
modules/fbx/data/pivot_transform.h
Normal file
@@ -0,0 +1,115 @@
|
||||
/*************************************************************************/
|
||||
/* pivot_transform.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 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.h"
|
||||
#include "core/object/reference.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 : Reference, 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.
|
||||
Quat pre_rotation = Quat();
|
||||
Quat post_rotation = Quat();
|
||||
Quat rotation = Quat();
|
||||
Quat geometric_rotation = Quat();
|
||||
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));
|
||||
}
|
||||
|
||||
Transform ComputeGlobalTransform(Transform t) const;
|
||||
Transform ComputeLocalTransform(Transform t) const;
|
||||
Transform ComputeGlobalTransform(Vector3 p_translation, Quat p_rotation, Vector3 p_scaling) const;
|
||||
Transform ComputeLocalTransform(Vector3 p_translation, Quat 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
|
||||
Transform GlobalTransform = Transform();
|
||||
Transform LocalTransform = Transform();
|
||||
Transform Local_Scaling_Matrix = Transform(); // used for inherit type.
|
||||
Transform GeometricTransform = Transform(); // 3DS max only
|
||||
FBXDocParser::TransformInheritance inherit_type = FBXDocParser::TransformInheritance_MAX; // maya fbx requires this - sorry <3
|
||||
};
|
||||
|
||||
#endif // PIVOT_TRANSFORM_H
|
||||
Reference in New Issue
Block a user