diff options
Diffstat (limited to 'modules')
723 files changed, 35138 insertions, 10949 deletions
diff --git a/modules/arkit/arkit_interface.h b/modules/arkit/arkit_interface.h index 29e09411ff..f9b7709aba 100644 --- a/modules/arkit/arkit_interface.h +++ b/modules/arkit/arkit_interface.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/arkit/arkit_interface.mm b/modules/arkit/arkit_interface.mm index e8fa023ac7..608afd4ff3 100644 --- a/modules/arkit/arkit_interface.mm +++ b/modules/arkit/arkit_interface.mm @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -398,14 +398,14 @@ XRPositionalTracker *ARKitInterface::get_anchor_for_uuid(const unsigned char *p_ } XRPositionalTracker *new_tracker = memnew(XRPositionalTracker); - new_tracker->set_type(XRServer::TRACKER_ANCHOR); + new_tracker->set_tracker_type(XRServer::TRACKER_ANCHOR); char tracker_name[256]; sprintf(tracker_name, "Anchor %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", p_uuid[0], p_uuid[1], p_uuid[2], p_uuid[3], p_uuid[4], p_uuid[5], p_uuid[6], p_uuid[7], p_uuid[8], p_uuid[9], p_uuid[10], p_uuid[11], p_uuid[12], p_uuid[13], p_uuid[14], p_uuid[15]); String name = tracker_name; print_line("Adding tracker " + name); - new_tracker->set_name(name); + new_tracker->set_tracker_name(name); // add our tracker XRServer::get_singleton()->add_tracker(new_tracker); @@ -712,8 +712,8 @@ void ARKitInterface::_add_or_update_anchor(GodotARAnchor *p_anchor) { int16_t index = planeAnchor.geometry.triangleIndices[j]; simd_float3 vrtx = planeAnchor.geometry.vertices[index]; simd_float2 textcoord = planeAnchor.geometry.textureCoordinates[index]; - surftool->add_uv(Vector2(textcoord[0], textcoord[1])); - surftool->add_color(Color(0.8, 0.8, 0.8)); + surftool->set_uv(Vector2(textcoord[0], textcoord[1])); + surftool->set_color(Color(0.8, 0.8, 0.8)); surftool->add_vertex(Vector3(vrtx[0], vrtx[1], vrtx[2])); } diff --git a/modules/arkit/arkit_module.cpp b/modules/arkit/arkit_module.cpp index 87ee3b87a5..be3c5e29ca 100644 --- a/modules/arkit/arkit_module.cpp +++ b/modules/arkit/arkit_module.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/arkit/arkit_module.h b/modules/arkit/arkit_module.h index 8aa8175ed5..ca48371152 100644 --- a/modules/arkit/arkit_module.h +++ b/modules/arkit/arkit_module.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/arkit/arkit_session_delegate.h b/modules/arkit/arkit_session_delegate.h index df98bf506e..f227d50b35 100644 --- a/modules/arkit/arkit_session_delegate.h +++ b/modules/arkit/arkit_session_delegate.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/arkit/arkit_session_delegate.mm b/modules/arkit/arkit_session_delegate.mm index f44f46b7b7..97af5bf42c 100644 --- a/modules/arkit/arkit_session_delegate.mm +++ b/modules/arkit/arkit_session_delegate.mm @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/assimp/SCsub b/modules/assimp/SCsub deleted file mode 100644 index f1d0c742b4..0000000000 --- a/modules/assimp/SCsub +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env python - -Import("env") -Import("env_modules") - -env_assimp = env_modules.Clone() - -# Force bundled version for now, there's no released version of Assimp with -# support for ArmaturePopulate which we use from their master branch. -if True: # env['builtin_assimp']: - thirdparty_dir = "#thirdparty/assimp" - - env_assimp.Prepend(CPPPATH=["#thirdparty/assimp"]) - env_assimp.Prepend(CPPPATH=["#thirdparty/assimp/code"]) - env_assimp.Prepend(CPPPATH=["#thirdparty/assimp/include"]) - - # env_assimp.Append(CPPDEFINES=['ASSIMP_DOUBLE_PRECISION']) # TODO default to what godot is compiled with for future double support - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_SINGLETHREADED"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_BOOST_WORKAROUND"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_OWN_ZLIB"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_EXPORT"]) - - # Importers we don't need - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_3D_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_3DS_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_3MF_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_AC_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_AMF_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_ASE_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_ASSBIN_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_B3D_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_BLEND_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_BVH_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_C4D_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_COB_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_COLLADA_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_CSM_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_DXF_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_GLTF2_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_GLTF_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_HMP_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_IFC_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_IRR_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_IRRMESH_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_LWO_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_LWS_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_M3D_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MD2_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MD3_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MD5_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MD5_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MDC_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MDL_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MMD_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MS3D_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_NDO_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_NFF_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_OBJ_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_OFF_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_OGRE_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_OPENGEX_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_PLY_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_Q3BSP_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_Q3D_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_RAW_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_SIB_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_SMD_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_STEP_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_STL_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_TERRAGEN_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_X3D_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_XGL_IMPORTER"]) - env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_X_IMPORTER"]) - - if env["platform"] == "windows": - env_assimp.Append(CPPDEFINES=["PLATFORM_WINDOWS"]) - env_assimp.Append(CPPDEFINES=[("PLATFORM", "WINDOWS")]) - elif env["platform"] == "linuxbsd": - env_assimp.Append(CPPDEFINES=["PLATFORM_LINUX"]) - env_assimp.Append(CPPDEFINES=[("PLATFORM", "LINUX")]) - elif env["platform"] == "osx": - env_assimp.Append(CPPDEFINES=["PLATFORM_DARWIN"]) - env_assimp.Append(CPPDEFINES=[("PLATFORM", "DARWIN")]) - - env_thirdparty = env_assimp.Clone() - env_thirdparty.disable_warnings() - env_thirdparty.add_source_files(env.modules_sources, Glob("#thirdparty/assimp/code/CApi/*.cpp")) - env_thirdparty.add_source_files(env.modules_sources, Glob("#thirdparty/assimp/code/Common/*.cpp")) - env_thirdparty.add_source_files(env.modules_sources, Glob("#thirdparty/assimp/code/PostProcessing/*.cpp")) - env_thirdparty.add_source_files(env.modules_sources, Glob("#thirdparty/assimp/code/Material/*.cpp")) - env_thirdparty.add_source_files(env.modules_sources, Glob("#thirdparty/assimp/code/FBX/*.cpp")) - -# Godot's own source files -env_assimp.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/assimp/editor_scene_importer_assimp.cpp b/modules/assimp/editor_scene_importer_assimp.cpp deleted file mode 100644 index e5becfd559..0000000000 --- a/modules/assimp/editor_scene_importer_assimp.cpp +++ /dev/null @@ -1,1486 +0,0 @@ -/*************************************************************************/ -/* editor_scene_importer_assimp.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 "editor_scene_importer_assimp.h" -#include "core/io/image_loader.h" -#include "editor/import/resource_importer_scene.h" -#include "import_utils.h" -#include "scene/3d/camera_3d.h" -#include "scene/3d/light_3d.h" -#include "scene/3d/mesh_instance_3d.h" -#include "scene/main/node.h" -#include "scene/resources/material.h" -#include "scene/resources/surface_tool.h" - -#include <assimp/matrix4x4.h> -#include <assimp/postprocess.h> -#include <assimp/scene.h> -#include <assimp/Importer.hpp> -#include <assimp/LogStream.hpp> - -// move into assimp -aiBone *get_bone_by_name(const aiScene *scene, aiString bone_name) { - for (unsigned int mesh_id = 0; mesh_id < scene->mNumMeshes; ++mesh_id) { - aiMesh *mesh = scene->mMeshes[mesh_id]; - - // iterate over all the bones on the mesh for this node only! - for (unsigned int boneIndex = 0; boneIndex < mesh->mNumBones; boneIndex++) { - aiBone *bone = mesh->mBones[boneIndex]; - if (bone->mName == bone_name) { - printf("matched bone by name: %s\n", bone->mName.C_Str()); - return bone; - } - } - } - - return nullptr; -} - -void EditorSceneImporterAssimp::get_extensions(List<String> *r_extensions) const { - const String import_setting_string = "filesystem/import/open_asset_import/"; - - Map<String, ImportFormat> import_format; - { - Vector<String> exts; - exts.push_back("fbx"); - ImportFormat import = { exts, true }; - import_format.insert("fbx", import); - } - for (Map<String, ImportFormat>::Element *E = import_format.front(); E; E = E->next()) { - _register_project_setting_import(E->key(), import_setting_string, E->get().extensions, r_extensions, - E->get().is_default); - } -} - -void EditorSceneImporterAssimp::_register_project_setting_import(const String generic, const String import_setting_string, - const Vector<String> &exts, List<String> *r_extensions, - const bool p_enabled) const { - const String use_generic = "use_" + generic; - _GLOBAL_DEF(import_setting_string + use_generic, p_enabled, true); - if (ProjectSettings::get_singleton()->get(import_setting_string + use_generic)) { - for (int32_t i = 0; i < exts.size(); i++) { - r_extensions->push_back(exts[i]); - } - } -} - -uint32_t EditorSceneImporterAssimp::get_import_flags() const { - return IMPORT_SCENE; -} - -void EditorSceneImporterAssimp::_bind_methods() { -} - -Node *EditorSceneImporterAssimp::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, - List<String> *r_missing_deps, Error *r_err) { - Assimp::Importer importer; - importer.SetPropertyBool(AI_CONFIG_PP_FD_REMOVE, true); - // Cannot remove pivot points because the static mesh will be in the wrong place - importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, false); - int32_t max_bone_weights = 4; - //if (p_flags & IMPORT_ANIMATION_EIGHT_WEIGHTS) { - // const int eight_bones = 8; - // importer.SetPropertyBool(AI_CONFIG_PP_LBW_MAX_WEIGHTS, eight_bones); - // max_bone_weights = eight_bones; - //} - - importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE | aiPrimitiveType_POINT); - - //importer.SetPropertyFloat(AI_CONFIG_PP_DB_THRESHOLD, 1.0f); - int32_t post_process_Steps = aiProcess_CalcTangentSpace | - aiProcess_GlobalScale | - // imports models and listens to their file scale for CM to M conversions - //aiProcess_FlipUVs | - aiProcess_FlipWindingOrder | - // very important for culling so that it is done in the correct order. - //aiProcess_DropNormals | - //aiProcess_GenSmoothNormals | - //aiProcess_JoinIdenticalVertices | - aiProcess_ImproveCacheLocality | - //aiProcess_RemoveRedundantMaterials | // Causes a crash - //aiProcess_SplitLargeMeshes | - aiProcess_Triangulate | - aiProcess_GenUVCoords | - //aiProcess_FindDegenerates | - //aiProcess_SortByPType | - // aiProcess_FindInvalidData | - aiProcess_TransformUVCoords | - aiProcess_FindInstances | - //aiProcess_FixInfacingNormals | - //aiProcess_ValidateDataStructure | - aiProcess_OptimizeMeshes | - aiProcess_PopulateArmatureData | - //aiProcess_OptimizeGraph | - //aiProcess_Debone | - // aiProcess_EmbedTextures | - //aiProcess_SplitByBoneCount | - 0; - String g_path = ProjectSettings::get_singleton()->globalize_path(p_path); - aiScene *scene = (aiScene *)importer.ReadFile(g_path.utf8().ptr(), post_process_Steps); - - ERR_FAIL_COND_V_MSG(scene == nullptr, nullptr, String("Open Asset Import failed to open: ") + String(importer.GetErrorString())); - - return _generate_scene(p_path, scene, p_flags, p_bake_fps, max_bone_weights); -} - -template <class T> -struct EditorSceneImporterAssetImportInterpolate { - T lerp(const T &a, const T &b, float c) const { - return a + (b - a) * c; - } - - T catmull_rom(const T &p0, const T &p1, const T &p2, const T &p3, float t) { - float t2 = t * t; - float t3 = t2 * t; - - return 0.5f * ((2.0f * p1) + (-p0 + p2) * t + (2.0f * p0 - 5.0f * p1 + 4.0f * p2 - p3) * t2 + (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3); - } - - T bezier(T start, T control_1, T control_2, T end, float t) { - /* Formula from Wikipedia article on Bezier curves. */ - real_t omt = (1.0 - t); - real_t omt2 = omt * omt; - real_t omt3 = omt2 * omt; - real_t t2 = t * t; - real_t t3 = t2 * t; - - return start * omt3 + control_1 * omt2 * t * 3.0 + control_2 * omt * t2 * 3.0 + end * t3; - } -}; - -//thank you for existing, partial specialization -template <> -struct EditorSceneImporterAssetImportInterpolate<Quat> { - Quat lerp(const Quat &a, const Quat &b, float c) const { - ERR_FAIL_COND_V_MSG(!a.is_normalized(), Quat(), "The quaternion \"a\" must be normalized."); - ERR_FAIL_COND_V_MSG(!b.is_normalized(), Quat(), "The quaternion \"b\" must be normalized."); - - return a.slerp(b, c).normalized(); - } - - Quat catmull_rom(const Quat &p0, const Quat &p1, const Quat &p2, const Quat &p3, float c) { - ERR_FAIL_COND_V_MSG(!p1.is_normalized(), Quat(), "The quaternion \"p1\" must be normalized."); - ERR_FAIL_COND_V_MSG(!p2.is_normalized(), Quat(), "The quaternion \"p2\" must be normalized."); - - return p1.slerp(p2, c).normalized(); - } - - Quat bezier(Quat start, Quat control_1, Quat control_2, Quat end, float t) { - ERR_FAIL_COND_V_MSG(!start.is_normalized(), Quat(), "The start quaternion must be normalized."); - ERR_FAIL_COND_V_MSG(!end.is_normalized(), Quat(), "The end quaternion must be normalized."); - - return start.slerp(end, t).normalized(); - } -}; - -template <class T> -T EditorSceneImporterAssimp::_interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time, - AssetImportAnimation::Interpolation p_interp) { - //could use binary search, worth it? - int idx = -1; - for (int i = 0; i < p_times.size(); i++) { - if (p_times[i] > p_time) { - break; - } - idx++; - } - - EditorSceneImporterAssetImportInterpolate<T> interp; - - switch (p_interp) { - case AssetImportAnimation::INTERP_LINEAR: { - if (idx == -1) { - return p_values[0]; - } else if (idx >= p_times.size() - 1) { - return p_values[p_times.size() - 1]; - } - - float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]); - - return interp.lerp(p_values[idx], p_values[idx + 1], c); - - } break; - case AssetImportAnimation::INTERP_STEP: { - if (idx == -1) { - return p_values[0]; - } else if (idx >= p_times.size() - 1) { - return p_values[p_times.size() - 1]; - } - - return p_values[idx]; - - } break; - case AssetImportAnimation::INTERP_CATMULLROMSPLINE: { - if (idx == -1) { - return p_values[1]; - } else if (idx >= p_times.size() - 1) { - return p_values[1 + p_times.size() - 1]; - } - - float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]); - - return interp.catmull_rom(p_values[idx - 1], p_values[idx], p_values[idx + 1], p_values[idx + 3], c); - - } break; - case AssetImportAnimation::INTERP_CUBIC_SPLINE: { - if (idx == -1) { - return p_values[1]; - } else if (idx >= p_times.size() - 1) { - return p_values[(p_times.size() - 1) * 3 + 1]; - } - - float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]); - - T from = p_values[idx * 3 + 1]; - T c1 = from + p_values[idx * 3 + 2]; - T to = p_values[idx * 3 + 4]; - T c2 = to + p_values[idx * 3 + 3]; - - return interp.bezier(from, c1, c2, to, c); - - } break; - } - - ERR_FAIL_V(p_values[0]); -} - -aiBone *EditorSceneImporterAssimp::get_bone_from_stack(ImportState &state, aiString name) { - List<aiBone *>::Element *iter; - aiBone *bone = nullptr; - for (iter = state.bone_stack.front(); iter; iter = iter->next()) { - bone = (aiBone *)iter->get(); - - if (bone && bone->mName == name) { - state.bone_stack.erase(bone); - return bone; - } - } - - return nullptr; -} - -Node3D * -EditorSceneImporterAssimp::_generate_scene(const String &p_path, aiScene *scene, const uint32_t p_flags, int p_bake_fps, - const int32_t p_max_bone_weights) { - ERR_FAIL_COND_V(scene == nullptr, nullptr); - - ImportState state; - state.path = p_path; - state.assimp_scene = scene; - state.max_bone_weights = p_max_bone_weights; - state.animation_player = nullptr; - state.import_flags = p_flags; - - // populate light map - for (unsigned int l = 0; l < scene->mNumLights; l++) { - aiLight *ai_light = scene->mLights[l]; - ERR_CONTINUE(ai_light == nullptr); - state.light_cache[AssimpUtils::get_assimp_string(ai_light->mName)] = l; - } - - // fill camera cache - for (unsigned int c = 0; c < scene->mNumCameras; c++) { - aiCamera *ai_camera = scene->mCameras[c]; - ERR_CONTINUE(ai_camera == nullptr); - state.camera_cache[AssimpUtils::get_assimp_string(ai_camera->mName)] = c; - } - - if (scene->mRootNode) { - state.nodes.push_back(scene->mRootNode); - - // make flat node tree - in order to make processing deterministic - for (unsigned int i = 0; i < scene->mRootNode->mNumChildren; i++) { - _generate_node(state, scene->mRootNode->mChildren[i]); - } - - RegenerateBoneStack(state); - - Node *last_valid_parent = nullptr; - - List<const aiNode *>::Element *iter; - for (iter = state.nodes.front(); iter; iter = iter->next()) { - const aiNode *element_assimp_node = iter->get(); - const aiNode *parent_assimp_node = element_assimp_node->mParent; - - String node_name = AssimpUtils::get_assimp_string(element_assimp_node->mName); - //print_verbose("node: " + node_name); - - Node3D *spatial = nullptr; - Transform transform = AssimpUtils::assimp_matrix_transform(element_assimp_node->mTransformation); - - // retrieve this node bone - aiBone *bone = get_bone_from_stack(state, element_assimp_node->mName); - - if (state.light_cache.has(node_name)) { - spatial = create_light(state, node_name, transform); - } else if (state.camera_cache.has(node_name)) { - spatial = create_camera(state, node_name, transform); - } else if (state.armature_nodes.find(element_assimp_node)) { - // create skeleton - print_verbose("Making skeleton: " + node_name); - Skeleton3D *skeleton = memnew(Skeleton3D); - spatial = skeleton; - if (!state.armature_skeletons.has(element_assimp_node)) { - state.armature_skeletons.insert(element_assimp_node, skeleton); - } - } else if (bone != nullptr) { - continue; - } else { - spatial = memnew(Node3D); - } - - ERR_CONTINUE_MSG(spatial == nullptr, "FBX Import - are we out of ram?"); - // we on purpose set the transform and name after creating the node. - - spatial->set_name(node_name); - spatial->set_global_transform(transform); - - // first element is root - if (iter == state.nodes.front()) { - state.root = spatial; - } - - // flat node map parent lookup tool - state.flat_node_map.insert(element_assimp_node, spatial); - - Map<const aiNode *, Node3D *>::Element *parent_lookup = state.flat_node_map.find(parent_assimp_node); - - // note: this always fails on the root node :) keep that in mind this is by design - if (parent_lookup) { - Node3D *parent_node = parent_lookup->value(); - - ERR_FAIL_COND_V_MSG(parent_node == nullptr, state.root, - "Parent node invalid even though lookup successful, out of ram?"); - - if (spatial != state.root) { - parent_node->add_child(spatial); - spatial->set_owner(state.root); - } else { - // required - think about it root never has a parent yet is valid, anything else without a parent is not valid. - } - } else if (spatial != state.root) { - // if the ainode is not in the tree - // parent it to the last good parent found - if (last_valid_parent) { - last_valid_parent->add_child(spatial); - spatial->set_owner(state.root); - } else { - // this is a serious error? - memdelete(spatial); - } - } - - // update last valid parent - last_valid_parent = spatial; - } - print_verbose("node counts: " + itos(state.nodes.size())); - - // make clean bone stack - RegenerateBoneStack(state); - - print_verbose("generating godot bone data"); - - print_verbose("Godot bone stack count: " + itos(state.bone_stack.size())); - - // This is a list of bones, duplicates are from other meshes and must be dealt with properly - for (List<aiBone *>::Element *element = state.bone_stack.front(); element; element = element->next()) { - aiBone *bone = element->get(); - - ERR_CONTINUE_MSG(!bone, "invalid bone read from assimp?"); - - // Utilities for armature lookup - for now only FBX makes these - aiNode *armature_for_bone = bone->mArmature; - - // Utilities for bone node lookup - for now only FBX makes these - aiNode *bone_node = bone->mNode; - aiNode *parent_node = bone_node->mParent; - - String bone_name = AssimpUtils::get_anim_string_from_assimp(bone->mName); - ERR_CONTINUE_MSG(armature_for_bone == nullptr, "Armature for bone invalid: " + bone_name); - Skeleton3D *skeleton = state.armature_skeletons[armature_for_bone]; - - state.skeleton_bone_map[bone] = skeleton; - - if (bone_name.empty()) { - bone_name = "untitled_bone_name"; - WARN_PRINT("Untitled bone name detected... report with file please"); - } - - // todo: this is where skin support goes - if (skeleton && skeleton->find_bone(bone_name) == -1) { - print_verbose("[Godot Glue] Imported bone" + bone_name); - int boneIdx = skeleton->get_bone_count(); - - Transform pform = AssimpUtils::assimp_matrix_transform(bone->mNode->mTransformation); - skeleton->add_bone(bone_name); - skeleton->set_bone_rest(boneIdx, pform); - - if (parent_node != nullptr) { - int parent_bone_id = skeleton->find_bone(AssimpUtils::get_anim_string_from_assimp(parent_node->mName)); - int current_bone_id = boneIdx; - skeleton->set_bone_parent(current_bone_id, parent_bone_id); - } - } - } - - print_verbose("generating mesh phase from skeletal mesh"); - - List<Node3D *> cleanup_template_nodes; - - for (Map<const aiNode *, Node3D *>::Element *key_value_pair = state.flat_node_map.front(); key_value_pair; key_value_pair = key_value_pair->next()) { - const aiNode *assimp_node = key_value_pair->key(); - Node3D *mesh_template = key_value_pair->value(); - - ERR_CONTINUE(assimp_node == nullptr); - ERR_CONTINUE(mesh_template == nullptr); - - Node *parent_node = mesh_template->get_parent(); - - if (mesh_template == state.root) { - continue; - } - - if (parent_node == nullptr) { - print_error("Found invalid parent node!"); - continue; // root node - } - - String node_name = AssimpUtils::get_assimp_string(assimp_node->mName); - Transform node_transform = AssimpUtils::assimp_matrix_transform(assimp_node->mTransformation); - - if (assimp_node->mNumMeshes > 0) { - MeshInstance3D *mesh = create_mesh(state, assimp_node, node_name, parent_node, node_transform); - if (mesh) { - parent_node->remove_child(mesh_template); - - // re-parent children - List<Node *> children; - // re-parent all children to new node - // note: since get_child_count will change during execution we must build a list first to be safe. - for (int childId = 0; childId < mesh_template->get_child_count(); childId++) { - // get child - Node *child = mesh_template->get_child(childId); - children.push_back(child); - } - - for (List<Node *>::Element *element = children.front(); element; element = element->next()) { - // reparent the children to the real mesh node. - mesh_template->remove_child(element->get()); - mesh->add_child(element->get()); - element->get()->set_owner(state.root); - } - - // update mesh in list so that each mesh node is available - // this makes the template unavailable which is the desired behaviour - state.flat_node_map[assimp_node] = mesh; - - cleanup_template_nodes.push_back(mesh_template); - - // clean up this list we don't need it - children.clear(); - } - } - } - - for (List<Node3D *>::Element *element = cleanup_template_nodes.front(); element; element = element->next()) { - if (element->get()) { - memdelete(element->get()); - } - } - } - - if (p_flags & IMPORT_ANIMATION && scene->mNumAnimations) { - state.animation_player = memnew(AnimationPlayer); - state.root->add_child(state.animation_player); - state.animation_player->set_owner(state.root); - - for (uint32_t i = 0; i < scene->mNumAnimations; i++) { - _import_animation(state, i, p_bake_fps); - } - } - - // - // Cleanup operations - // - - state.mesh_cache.clear(); - state.material_cache.clear(); - state.light_cache.clear(); - state.camera_cache.clear(); - state.assimp_node_map.clear(); - state.path_to_image_cache.clear(); - state.nodes.clear(); - state.flat_node_map.clear(); - state.armature_skeletons.clear(); - state.bone_stack.clear(); - return state.root; -} - -void EditorSceneImporterAssimp::_insert_animation_track(ImportState &scene, const aiAnimation *assimp_anim, int track_id, - int anim_fps, Ref<Animation> animation, float ticks_per_second, - Skeleton3D *skeleton, const NodePath &node_path, - const String &node_name, aiBone *track_bone) { - const aiNodeAnim *assimp_track = assimp_anim->mChannels[track_id]; - //make transform track - int track_idx = animation->get_track_count(); - animation->add_track(Animation::TYPE_TRANSFORM); - animation->track_set_path(track_idx, node_path); - //first determine animation length - - float increment = 1.0 / float(anim_fps); - float time = 0.0; - - bool last = false; - - Vector<Vector3> pos_values; - Vector<float> pos_times; - Vector<Vector3> scale_values; - Vector<float> scale_times; - Vector<Quat> rot_values; - Vector<float> rot_times; - - for (size_t p = 0; p < assimp_track->mNumPositionKeys; p++) { - aiVector3D pos = assimp_track->mPositionKeys[p].mValue; - pos_values.push_back(Vector3(pos.x, pos.y, pos.z)); - pos_times.push_back(assimp_track->mPositionKeys[p].mTime / ticks_per_second); - } - - for (size_t r = 0; r < assimp_track->mNumRotationKeys; r++) { - aiQuaternion quat = assimp_track->mRotationKeys[r].mValue; - rot_values.push_back(Quat(quat.x, quat.y, quat.z, quat.w).normalized()); - rot_times.push_back(assimp_track->mRotationKeys[r].mTime / ticks_per_second); - } - - for (size_t sc = 0; sc < assimp_track->mNumScalingKeys; sc++) { - aiVector3D scale = assimp_track->mScalingKeys[sc].mValue; - scale_values.push_back(Vector3(scale.x, scale.y, scale.z)); - scale_times.push_back(assimp_track->mScalingKeys[sc].mTime / ticks_per_second); - } - - while (true) { - Vector3 pos; - Quat rot; - Vector3 scale(1, 1, 1); - - if (pos_values.size()) { - pos = _interpolate_track<Vector3>(pos_times, pos_values, time, AssetImportAnimation::INTERP_LINEAR); - } - - if (rot_values.size()) { - rot = _interpolate_track<Quat>(rot_times, rot_values, time, - AssetImportAnimation::INTERP_LINEAR) - .normalized(); - } - - if (scale_values.size()) { - scale = _interpolate_track<Vector3>(scale_times, scale_values, time, AssetImportAnimation::INTERP_LINEAR); - } - - if (skeleton) { - int skeleton_bone = skeleton->find_bone(node_name); - - if (skeleton_bone >= 0 && track_bone) { - Transform xform; - xform.basis.set_quat_scale(rot, scale); - xform.origin = pos; - - xform = skeleton->get_bone_rest(skeleton_bone).inverse() * xform; - - rot = xform.basis.get_rotation_quat(); - rot.normalize(); - scale = xform.basis.get_scale(); - pos = xform.origin; - } else { - ERR_FAIL_MSG("Skeleton bone lookup failed for skeleton: " + skeleton->get_name()); - } - } - - animation->track_set_interpolation_type(track_idx, Animation::INTERPOLATION_LINEAR); - animation->transform_track_insert_key(track_idx, time, pos, rot, scale); - - if (last) { //done this way so a key is always inserted past the end (for proper interpolation) - break; - } - time += increment; - if (time >= animation->get_length()) { - last = true; - } - } -} - -// I really do not like this but need to figure out a better way of removing it later. -Node *EditorSceneImporterAssimp::get_node_by_name(ImportState &state, String name) { - for (Map<const aiNode *, Node3D *>::Element *key_value_pair = state.flat_node_map.front(); key_value_pair; key_value_pair = key_value_pair->next()) { - const aiNode *assimp_node = key_value_pair->key(); - Node3D *node = key_value_pair->value(); - - String node_name = AssimpUtils::get_assimp_string(assimp_node->mName); - if (name == node_name && node) { - return node; - } - } - return nullptr; -} - -/* Bone stack is a fifo handler for multiple armatures since armatures aren't a thing in assimp (yet) */ -void EditorSceneImporterAssimp::RegenerateBoneStack(ImportState &state) { - state.bone_stack.clear(); - // build bone stack list - for (unsigned int mesh_id = 0; mesh_id < state.assimp_scene->mNumMeshes; ++mesh_id) { - aiMesh *mesh = state.assimp_scene->mMeshes[mesh_id]; - - // iterate over all the bones on the mesh for this node only! - for (unsigned int boneIndex = 0; boneIndex < mesh->mNumBones; boneIndex++) { - aiBone *bone = mesh->mBones[boneIndex]; - - // doubtful this is required right now but best to check - if (!state.bone_stack.find(bone)) { - //print_verbose("[assimp] bone stack added: " + String(bone->mName.C_Str()) ); - state.bone_stack.push_back(bone); - } - } - } -} - -/* Bone stack is a fifo handler for multiple armatures since armatures aren't a thing in assimp (yet) */ -void EditorSceneImporterAssimp::RegenerateBoneStack(ImportState &state, aiMesh *mesh) { - state.bone_stack.clear(); - // iterate over all the bones on the mesh for this node only! - for (unsigned int boneIndex = 0; boneIndex < mesh->mNumBones; boneIndex++) { - aiBone *bone = mesh->mBones[boneIndex]; - if (state.bone_stack.find(bone) == nullptr) { - state.bone_stack.push_back(bone); - } - } -} - -// animation tracks are per bone - -void EditorSceneImporterAssimp::_import_animation(ImportState &state, int p_animation_index, int p_bake_fps) { - ERR_FAIL_INDEX(p_animation_index, (int)state.assimp_scene->mNumAnimations); - - const aiAnimation *anim = state.assimp_scene->mAnimations[p_animation_index]; - String name = AssimpUtils::get_anim_string_from_assimp(anim->mName); - if (name == String()) { - name = "Animation " + itos(p_animation_index + 1); - } - print_verbose("import animation: " + name); - float ticks_per_second = anim->mTicksPerSecond; - - if (state.assimp_scene->mMetaData != nullptr && Math::is_equal_approx(ticks_per_second, 0.0f)) { - int32_t time_mode = 0; - state.assimp_scene->mMetaData->Get("TimeMode", time_mode); - ticks_per_second = AssimpUtils::get_fbx_fps(time_mode, state.assimp_scene); - } - - //? - //if ((p_path.get_file().get_extension().to_lower() == "glb" || p_path.get_file().get_extension().to_lower() == "gltf") && Math::is_equal_approx(ticks_per_second, 0.0f)) { - // ticks_per_second = 1000.0f; - //} - - if (Math::is_equal_approx(ticks_per_second, 0.0f)) { - ticks_per_second = 25.0f; - } - - Ref<Animation> animation; - animation.instance(); - animation->set_name(name); - animation->set_length(anim->mDuration / ticks_per_second); - - if (name.begins_with("loop") || name.ends_with("loop") || name.begins_with("cycle") || name.ends_with("cycle")) { - animation->set_loop(true); - } - - // generate bone stack for animation import - RegenerateBoneStack(state); - - //regular tracks - for (size_t i = 0; i < anim->mNumChannels; i++) { - const aiNodeAnim *track = anim->mChannels[i]; - String node_name = AssimpUtils::get_assimp_string(track->mNodeName); - print_verbose("track name import: " + node_name); - if (track->mNumRotationKeys == 0 && track->mNumPositionKeys == 0 && track->mNumScalingKeys == 0) { - continue; //do not bother - } - - Skeleton3D *skeleton = nullptr; - NodePath node_path; - aiBone *bone = nullptr; - - // Import skeleton bone animation for this track - // Any bone will do, no point in processing more than just what is in the skeleton - { - bone = get_bone_from_stack(state, track->mNodeName); - - if (bone) { - // get skeleton by bone - skeleton = state.armature_skeletons[bone->mArmature]; - - if (skeleton) { - String path = state.root->get_path_to(skeleton); - path += ":" + node_name; - node_path = path; - - if (node_path != NodePath()) { - _insert_animation_track(state, anim, i, p_bake_fps, animation, ticks_per_second, skeleton, - node_path, node_name, bone); - } else { - print_error("Failed to find valid node path for animation"); - } - } - } - } - - // not a bone - // note this is flaky it uses node names which is unreliable - Node *allocated_node = get_node_by_name(state, node_name); - // todo: implement skeleton grabbing for node based animations too :) - // check if node exists, if it does then also apply animation track for node and bones above are all handled. - // this is now inclusive animation handling so that - // we import all the data and do not miss anything. - if (allocated_node) { - node_path = state.root->get_path_to(allocated_node); - - if (node_path != NodePath()) { - _insert_animation_track(state, anim, i, p_bake_fps, animation, ticks_per_second, skeleton, - node_path, node_name, nullptr); - } - } - } - - //blend shape tracks - - for (size_t i = 0; i < anim->mNumMorphMeshChannels; i++) { - const aiMeshMorphAnim *anim_mesh = anim->mMorphMeshChannels[i]; - - const String prop_name = AssimpUtils::get_assimp_string(anim_mesh->mName); - const String mesh_name = prop_name.split("*")[0]; - - ERR_CONTINUE(prop_name.split("*").size() != 2); - - Node *item = get_node_by_name(state, mesh_name); - ERR_CONTINUE_MSG(!item, "failed to look up node by name"); - const MeshInstance3D *mesh_instance = Object::cast_to<MeshInstance3D>(item); - ERR_CONTINUE(mesh_instance == nullptr); - - String base_path = state.root->get_path_to(mesh_instance); - - Ref<Mesh> mesh = mesh_instance->get_mesh(); - ERR_CONTINUE(mesh.is_null()); - - //add the tracks for this mesh - int base_track = animation->get_track_count(); - for (int j = 0; j < mesh->get_blend_shape_count(); j++) { - animation->add_track(Animation::TYPE_VALUE); - animation->track_set_path(base_track + j, base_path + ":blend_shapes/" + mesh->get_blend_shape_name(j)); - } - - for (size_t k = 0; k < anim_mesh->mNumKeys; k++) { - for (size_t j = 0; j < anim_mesh->mKeys[k].mNumValuesAndWeights; j++) { - float t = anim_mesh->mKeys[k].mTime / ticks_per_second; - float w = anim_mesh->mKeys[k].mWeights[j]; - - animation->track_insert_key(base_track + j, t, w); - } - } - } - - if (animation->get_track_count()) { - state.animation_player->add_animation(name, animation); - } -} - -// -// Mesh Generation from indices ? why do we need so much mesh code -// [debt needs looked into] -Ref<Mesh> -EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(ImportState &state, const Vector<int> &p_surface_indices, - const aiNode *assimp_node, Ref<Skin> &skin, - Skeleton3D *&skeleton_assigned) { - Ref<ArrayMesh> mesh; - mesh.instance(); - bool has_uvs = false; - bool compress_vert_data = state.import_flags & IMPORT_USE_COMPRESSION; - uint32_t mesh_flags = compress_vert_data ? Mesh::ARRAY_COMPRESS_DEFAULT : 0; - - Map<String, uint32_t> morph_mesh_string_lookup; - - for (int i = 0; i < p_surface_indices.size(); i++) { - const unsigned int mesh_idx = p_surface_indices[0]; - const aiMesh *ai_mesh = state.assimp_scene->mMeshes[mesh_idx]; - for (size_t j = 0; j < ai_mesh->mNumAnimMeshes; j++) { - String ai_anim_mesh_name = AssimpUtils::get_assimp_string(ai_mesh->mAnimMeshes[j]->mName); - if (!morph_mesh_string_lookup.has(ai_anim_mesh_name)) { - morph_mesh_string_lookup.insert(ai_anim_mesh_name, j); - mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_NORMALIZED); - if (ai_anim_mesh_name.empty()) { - ai_anim_mesh_name = String("morph_") + itos(j); - } - mesh->add_blend_shape(ai_anim_mesh_name); - } - } - } - // - // Process Vertex Weights - // - for (int i = 0; i < p_surface_indices.size(); i++) { - const unsigned int mesh_idx = p_surface_indices[i]; - const aiMesh *ai_mesh = state.assimp_scene->mMeshes[mesh_idx]; - - Map<uint32_t, Vector<BoneInfo>> vertex_weights; - - if (ai_mesh->mNumBones > 0) { - for (size_t b = 0; b < ai_mesh->mNumBones; b++) { - aiBone *bone = ai_mesh->mBones[b]; - - if (!skeleton_assigned) { - print_verbose("Assigned mesh skeleton during mesh creation"); - skeleton_assigned = state.skeleton_bone_map[bone]; - - if (!skin.is_valid()) { - print_verbose("Configured new skin"); - skin.instance(); - } else { - print_verbose("Reusing existing skin!"); - } - } - // skeleton_assigned = - String bone_name = AssimpUtils::get_assimp_string(bone->mName); - int bone_index = skeleton_assigned->find_bone(bone_name); - ERR_CONTINUE(bone_index == -1); - for (size_t w = 0; w < bone->mNumWeights; w++) { - aiVertexWeight ai_weights = bone->mWeights[w]; - - BoneInfo bi; - uint32_t vertex_index = ai_weights.mVertexId; - bi.bone = bone_index; - bi.weight = ai_weights.mWeight; - - if (!vertex_weights.has(vertex_index)) { - vertex_weights[vertex_index] = Vector<BoneInfo>(); - } - - vertex_weights[vertex_index].push_back(bi); - } - } - } - - // - // Create mesh from data from assimp - // - - Ref<SurfaceTool> st; - st.instance(); - st->begin(Mesh::PRIMITIVE_TRIANGLES); - - for (size_t j = 0; j < ai_mesh->mNumVertices; j++) { - // Get the texture coordinates if they exist - if (ai_mesh->HasTextureCoords(0)) { - has_uvs = true; - st->add_uv(Vector2(ai_mesh->mTextureCoords[0][j].x, 1.0f - ai_mesh->mTextureCoords[0][j].y)); - } - - if (ai_mesh->HasTextureCoords(1)) { - has_uvs = true; - st->add_uv2(Vector2(ai_mesh->mTextureCoords[1][j].x, 1.0f - ai_mesh->mTextureCoords[1][j].y)); - } - - // Assign vertex colors - if (ai_mesh->HasVertexColors(0)) { - Color color = Color(ai_mesh->mColors[0]->r, ai_mesh->mColors[0]->g, ai_mesh->mColors[0]->b, - ai_mesh->mColors[0]->a); - st->add_color(color); - } - - // Work out normal calculations? - this needs work it doesn't work properly on huestos - if (ai_mesh->mNormals != nullptr) { - const aiVector3D normals = ai_mesh->mNormals[j]; - const Vector3 godot_normal = Vector3(normals.x, normals.y, normals.z); - st->add_normal(godot_normal); - if (ai_mesh->HasTangentsAndBitangents()) { - const aiVector3D tangents = ai_mesh->mTangents[j]; - const Vector3 godot_tangent = Vector3(tangents.x, tangents.y, tangents.z); - const aiVector3D bitangent = ai_mesh->mBitangents[j]; - const Vector3 godot_bitangent = Vector3(bitangent.x, bitangent.y, bitangent.z); - float d = godot_normal.cross(godot_tangent).dot(godot_bitangent) > 0.0f ? 1.0f : -1.0f; - st->add_tangent(Plane(tangents.x, tangents.y, tangents.z, d)); - } - } - - // We have vertex weights right? - if (vertex_weights.has(j)) { - Vector<BoneInfo> bone_info = vertex_weights[j]; - Vector<int> bones; - bones.resize(bone_info.size()); - Vector<float> weights; - weights.resize(bone_info.size()); - - // todo? do we really need to loop over all bones? - assimp may have helper to find all influences on this vertex. - for (int k = 0; k < bone_info.size(); k++) { - bones.write[k] = bone_info[k].bone; - weights.write[k] = bone_info[k].weight; - } - - st->add_bones(bones); - st->add_weights(weights); - } - - // Assign vertex - const aiVector3D pos = ai_mesh->mVertices[j]; - - // note we must include node offset transform as this is relative to world space not local space. - Vector3 godot_pos = Vector3(pos.x, pos.y, pos.z); - st->add_vertex(godot_pos); - } - - // fire replacement for face handling - for (size_t j = 0; j < ai_mesh->mNumFaces; j++) { - const aiFace face = ai_mesh->mFaces[j]; - for (unsigned int k = 0; k < face.mNumIndices; k++) { - st->add_index(face.mIndices[k]); - } - } - - if (ai_mesh->HasTangentsAndBitangents() == false && has_uvs) { - st->generate_tangents(); - } - - aiMaterial *ai_material = state.assimp_scene->mMaterials[ai_mesh->mMaterialIndex]; - Ref<StandardMaterial3D> mat; - mat.instance(); - - int32_t mat_two_sided = 0; - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_TWOSIDED, mat_two_sided)) { - if (mat_two_sided > 0) { - mat->set_cull_mode(StandardMaterial3D::CULL_DISABLED); - } else { - mat->set_cull_mode(StandardMaterial3D::CULL_BACK); - } - } - - aiString mat_name; - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_NAME, mat_name)) { - mat->set_name(AssimpUtils::get_assimp_string(mat_name)); - } - - // Culling handling for meshes - - // cull all back faces - mat->set_cull_mode(StandardMaterial3D::CULL_DISABLED); - - // Now process materials - aiTextureType base_color = aiTextureType_BASE_COLOR; - { - String filename, path; - AssimpImageData image_data; - - if (AssimpUtils::GetAssimpTexture(state, ai_material, base_color, filename, path, image_data)) { - AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture); - - // anything transparent must be culled - if (image_data.raw_image->detect_alpha() != Image::ALPHA_NONE) { - mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA_DEPTH_PRE_PASS); - mat->set_cull_mode(StandardMaterial3D::CULL_DISABLED); // since you can see both sides in transparent mode - } - - mat->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, image_data.texture); - } - } - - aiTextureType tex_diffuse = aiTextureType_DIFFUSE; - { - String filename, path; - AssimpImageData image_data; - - if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_diffuse, filename, path, image_data)) { - AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture); - - // anything transparent must be culled - if (image_data.raw_image->detect_alpha() != Image::ALPHA_NONE) { - mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA_DEPTH_PRE_PASS); - mat->set_cull_mode(StandardMaterial3D::CULL_DISABLED); // since you can see both sides in transparent mode - } - - mat->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, image_data.texture); - } - - aiColor4D clr_diffuse; - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_COLOR_DIFFUSE, clr_diffuse)) { - if (Math::is_equal_approx(clr_diffuse.a, 1.0f) == false) { - mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA_DEPTH_PRE_PASS); - mat->set_cull_mode(StandardMaterial3D::CULL_DISABLED); // since you can see both sides in transparent mode - } - mat->set_albedo(Color(clr_diffuse.r, clr_diffuse.g, clr_diffuse.b, clr_diffuse.a)); - } - } - - aiTextureType tex_normal = aiTextureType_NORMALS; - { - String filename, path; - Ref<ImageTexture> texture; - AssimpImageData image_data; - - // Process texture normal map - if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_normal, filename, path, image_data)) { - AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture); - mat->set_feature(StandardMaterial3D::Feature::FEATURE_NORMAL_MAPPING, true); - mat->set_texture(StandardMaterial3D::TEXTURE_NORMAL, image_data.texture); - } else { - aiString texture_path; - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_NORMAL_TEXTURE, AI_PROPERTIES, texture_path)) { - if (AssimpUtils::CreateAssimpTexture(state, texture_path, filename, path, image_data)) { - mat->set_feature(StandardMaterial3D::Feature::FEATURE_NORMAL_MAPPING, true); - mat->set_texture(StandardMaterial3D::TEXTURE_NORMAL, image_data.texture); - } - } - } - } - - aiTextureType tex_normal_camera = aiTextureType_NORMAL_CAMERA; - { - String filename, path; - Ref<ImageTexture> texture; - AssimpImageData image_data; - - // Process texture normal map - if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_normal_camera, filename, path, image_data)) { - AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture); - mat->set_feature(StandardMaterial3D::Feature::FEATURE_NORMAL_MAPPING, true); - mat->set_texture(StandardMaterial3D::TEXTURE_NORMAL, image_data.texture); - } - } - - aiTextureType tex_emission_color = aiTextureType_EMISSION_COLOR; - { - String filename, path; - Ref<ImageTexture> texture; - AssimpImageData image_data; - - // Process texture normal map - if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_emission_color, filename, path, image_data)) { - AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture); - mat->set_feature(StandardMaterial3D::Feature::FEATURE_NORMAL_MAPPING, true); - mat->set_texture(StandardMaterial3D::TEXTURE_NORMAL, image_data.texture); - } - } - - aiTextureType tex_metalness = aiTextureType_METALNESS; - { - String filename, path; - Ref<ImageTexture> texture; - AssimpImageData image_data; - - // Process texture normal map - if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_metalness, filename, path, image_data)) { - AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture); - mat->set_texture(StandardMaterial3D::TEXTURE_METALLIC, image_data.texture); - } - } - - aiTextureType tex_roughness = aiTextureType_DIFFUSE_ROUGHNESS; - { - String filename, path; - Ref<ImageTexture> texture; - AssimpImageData image_data; - - // Process texture normal map - if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_roughness, filename, path, image_data)) { - AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture); - mat->set_texture(StandardMaterial3D::TEXTURE_ROUGHNESS, image_data.texture); - } - } - - aiTextureType tex_emissive = aiTextureType_EMISSIVE; - { - String filename = ""; - String path = ""; - Ref<Image> texture; - AssimpImageData image_data; - - if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_emissive, filename, path, image_data)) { - AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture); - mat->set_feature(StandardMaterial3D::FEATURE_EMISSION, true); - mat->set_texture(StandardMaterial3D::TEXTURE_EMISSION, image_data.texture); - } else { - // Process emission textures - aiString texture_emissive_path; - if (AI_SUCCESS == - ai_material->Get(AI_MATKEY_FBX_MAYA_EMISSION_TEXTURE, AI_PROPERTIES, texture_emissive_path)) { - if (AssimpUtils::CreateAssimpTexture(state, texture_emissive_path, filename, path, image_data)) { - mat->set_feature(StandardMaterial3D::FEATURE_EMISSION, true); - mat->set_texture(StandardMaterial3D::TEXTURE_EMISSION, image_data.texture); - } - } else { - float pbr_emission = 0.0f; - if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_EMISSIVE_FACTOR, AI_NULL, pbr_emission)) { - mat->set_emission(Color(pbr_emission, pbr_emission, pbr_emission, 1.0f)); - } - } - } - } - - aiTextureType tex_specular = aiTextureType_SPECULAR; - { - String filename, path; - Ref<ImageTexture> texture; - AssimpImageData image_data; - - // Process texture normal map - if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_specular, filename, path, image_data)) { - AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture); - mat->set_texture(StandardMaterial3D::TEXTURE_METALLIC, image_data.texture); - } - } - - aiTextureType tex_ao_map = aiTextureType_AMBIENT_OCCLUSION; - { - String filename, path; - Ref<ImageTexture> texture; - AssimpImageData image_data; - - // Process texture normal map - if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_ao_map, filename, path, image_data)) { - AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture); - mat->set_feature(StandardMaterial3D::FEATURE_AMBIENT_OCCLUSION, true); - mat->set_texture(StandardMaterial3D::TEXTURE_AMBIENT_OCCLUSION, image_data.texture); - } - } - - Array array_mesh = st->commit_to_arrays(); - Array morphs; - morphs.resize(ai_mesh->mNumAnimMeshes); - Mesh::PrimitiveType primitive = Mesh::PRIMITIVE_TRIANGLES; - - for (size_t j = 0; j < ai_mesh->mNumAnimMeshes; j++) { - String ai_anim_mesh_name = AssimpUtils::get_assimp_string(ai_mesh->mAnimMeshes[j]->mName); - - if (ai_anim_mesh_name.empty()) { - ai_anim_mesh_name = String("morph_") + itos(j); - } - - Array array_copy; - array_copy.resize(RenderingServer::ARRAY_MAX); - - for (int l = 0; l < RenderingServer::ARRAY_MAX; l++) { - array_copy[l] = array_mesh[l].duplicate(true); - } - - const size_t num_vertices = ai_mesh->mAnimMeshes[j]->mNumVertices; - array_copy[Mesh::ARRAY_INDEX] = Variant(); - if (ai_mesh->mAnimMeshes[j]->HasPositions()) { - PackedVector3Array vertices; - vertices.resize(num_vertices); - for (size_t l = 0; l < num_vertices; l++) { - const aiVector3D ai_pos = ai_mesh->mAnimMeshes[j]->mVertices[l]; - Vector3 position = Vector3(ai_pos.x, ai_pos.y, ai_pos.z); - vertices.ptrw()[l] = position; - } - PackedVector3Array new_vertices = array_copy[RenderingServer::ARRAY_VERTEX].duplicate(true); - ERR_CONTINUE(vertices.size() != new_vertices.size()); - for (int32_t l = 0; l < new_vertices.size(); l++) { - Vector3 *w = new_vertices.ptrw(); - w[l] = vertices[l]; - } - array_copy[RenderingServer::ARRAY_VERTEX] = new_vertices; - } - - int32_t color_set = 0; - if (ai_mesh->mAnimMeshes[j]->HasVertexColors(color_set)) { - PackedColorArray colors; - colors.resize(num_vertices); - for (size_t l = 0; l < num_vertices; l++) { - const aiColor4D ai_color = ai_mesh->mAnimMeshes[j]->mColors[color_set][l]; - Color color = Color(ai_color.r, ai_color.g, ai_color.b, ai_color.a); - colors.ptrw()[l] = color; - } - PackedColorArray new_colors = array_copy[RenderingServer::ARRAY_COLOR].duplicate(true); - ERR_CONTINUE(colors.size() != new_colors.size()); - for (int32_t l = 0; l < colors.size(); l++) { - Color *w = new_colors.ptrw(); - w[l] = colors[l]; - } - array_copy[RenderingServer::ARRAY_COLOR] = new_colors; - } - - if (ai_mesh->mAnimMeshes[j]->HasNormals()) { - PackedVector3Array normals; - normals.resize(num_vertices); - for (size_t l = 0; l < num_vertices; l++) { - const aiVector3D ai_normal = ai_mesh->mAnimMeshes[j]->mNormals[l]; - Vector3 normal = Vector3(ai_normal.x, ai_normal.y, ai_normal.z); - normals.ptrw()[l] = normal; - } - PackedVector3Array new_normals = array_copy[RenderingServer::ARRAY_NORMAL].duplicate(true); - ERR_CONTINUE(normals.size() != new_normals.size()); - for (int l = 0; l < normals.size(); l++) { - Vector3 *w = new_normals.ptrw(); - w[l] = normals[l]; - } - array_copy[RenderingServer::ARRAY_NORMAL] = new_normals; - } - - if (ai_mesh->mAnimMeshes[j]->HasTangentsAndBitangents()) { - PackedColorArray tangents; - tangents.resize(num_vertices); - Color *w = tangents.ptrw(); - for (size_t l = 0; l < num_vertices; l++) { - AssimpUtils::calc_tangent_from_mesh(ai_mesh, j, l, l, w); - } - PackedFloat32Array new_tangents = array_copy[RenderingServer::ARRAY_TANGENT].duplicate(true); - ERR_CONTINUE(new_tangents.size() != tangents.size() * 4); - for (int32_t l = 0; l < tangents.size(); l++) { - new_tangents.ptrw()[l + 0] = tangents[l].r; - new_tangents.ptrw()[l + 1] = tangents[l].g; - new_tangents.ptrw()[l + 2] = tangents[l].b; - new_tangents.ptrw()[l + 3] = tangents[l].a; - } - array_copy[RenderingServer::ARRAY_TANGENT] = new_tangents; - } - - morphs[j] = array_copy; - } - mesh->add_surface_from_arrays(primitive, array_mesh, morphs, Dictionary(), mesh_flags); - mesh->surface_set_material(i, mat); - mesh->surface_set_name(i, AssimpUtils::get_assimp_string(ai_mesh->mName)); - } - - return mesh; -} - -/** - * Create a new mesh for the node supplied - */ -MeshInstance3D * -EditorSceneImporterAssimp::create_mesh(ImportState &state, const aiNode *assimp_node, const String &node_name, Node *active_node, Transform node_transform) { - /* MESH NODE */ - Ref<Mesh> mesh; - Ref<Skin> skin; - // see if we have mesh cache for this. - Vector<int> surface_indices; - - RegenerateBoneStack(state); - - // Configure indices - for (uint32_t i = 0; i < assimp_node->mNumMeshes; i++) { - int mesh_index = assimp_node->mMeshes[i]; - // create list of mesh indexes - surface_indices.push_back(mesh_index); - } - - //surface_indices.sort(); - String mesh_key; - for (int i = 0; i < surface_indices.size(); i++) { - if (i > 0) { - mesh_key += ":"; - } - mesh_key += itos(surface_indices[i]); - } - - Skeleton3D *skeleton = nullptr; - aiNode *armature = nullptr; - - if (!state.mesh_cache.has(mesh_key)) { - mesh = _generate_mesh_from_surface_indices(state, surface_indices, assimp_node, skin, skeleton); - state.mesh_cache[mesh_key] = mesh; - } - - MeshInstance3D *mesh_node = memnew(MeshInstance3D); - mesh = state.mesh_cache[mesh_key]; - mesh_node->set_mesh(mesh); - - // if we have a valid skeleton set it up - if (skin.is_valid()) { - for (uint32_t i = 0; i < assimp_node->mNumMeshes; i++) { - unsigned int mesh_index = assimp_node->mMeshes[i]; - const aiMesh *ai_mesh = state.assimp_scene->mMeshes[mesh_index]; - - // please remember bone id relative to the skin is NOT the mesh relative index. - // it is the index relative to the skeleton that is why - // we have state.bone_id_map, it allows for duplicate bone id's too :) - // hope this makes sense - - int bind_count = 0; - for (unsigned int boneId = 0; boneId < ai_mesh->mNumBones; ++boneId) { - aiBone *iterBone = ai_mesh->mBones[boneId]; - - // used to reparent mesh to the correct armature later on if assigned. - if (!armature) { - print_verbose("Configured mesh armature, will reparent later to armature"); - armature = iterBone->mArmature; - } - - if (skeleton) { - int id = skeleton->find_bone(AssimpUtils::get_assimp_string(iterBone->mName)); - if (id != -1) { - print_verbose("Set bind bone: mesh: " + itos(mesh_index) + " bone index: " + itos(id)); - Transform t = AssimpUtils::assimp_matrix_transform(iterBone->mOffsetMatrix); - - skin->add_bind(bind_count, t); - skin->set_bind_bone(bind_count, id); - bind_count++; - } - } - } - } - - print_verbose("Finished configuring bind pose for skin mesh"); - } - - // this code parents all meshes with bones to the armature they are for - // GLTF2 specification relies on this and we are enforcing it for FBX. - if (armature && state.flat_node_map[armature]) { - Node *armature_parent = state.flat_node_map[armature]; - print_verbose("Parented mesh " + node_name + " to armature " + armature_parent->get_name()); - // static mesh handling - armature_parent->add_child(mesh_node); - // transform must be identity - mesh_node->set_global_transform(Transform()); - mesh_node->set_name(node_name); - mesh_node->set_owner(state.root); - } else { - // static mesh handling - active_node->add_child(mesh_node); - mesh_node->set_global_transform(node_transform); - mesh_node->set_name(node_name); - mesh_node->set_owner(state.root); - } - - if (skeleton) { - print_verbose("Attempted to set skeleton path!"); - mesh_node->set_skeleton_path(mesh_node->get_path_to(skeleton)); - mesh_node->set_skin(skin); - } - - return mesh_node; -} - -/** - * Create a light for the scene - * Automatically caches lights for lookup later - */ -Node3D *EditorSceneImporterAssimp::create_light( - ImportState &state, - const String &node_name, - Transform &look_at_transform) { - Light3D *light = nullptr; - aiLight *assimp_light = state.assimp_scene->mLights[state.light_cache[node_name]]; - ERR_FAIL_COND_V(!assimp_light, nullptr); - - if (assimp_light->mType == aiLightSource_DIRECTIONAL) { - light = memnew(DirectionalLight3D); - } else if (assimp_light->mType == aiLightSource_POINT) { - light = memnew(OmniLight3D); - } else if (assimp_light->mType == aiLightSource_SPOT) { - light = memnew(SpotLight3D); - } - ERR_FAIL_COND_V(light == nullptr, nullptr); - - if (assimp_light->mType != aiLightSource_POINT) { - Vector3 pos = Vector3( - assimp_light->mPosition.x, - assimp_light->mPosition.y, - assimp_light->mPosition.z); - Vector3 look_at = Vector3( - assimp_light->mDirection.y, - assimp_light->mDirection.x, - assimp_light->mDirection.z) - .normalized(); - Vector3 up = Vector3( - assimp_light->mUp.x, - assimp_light->mUp.y, - assimp_light->mUp.z); - - look_at_transform.set_look_at(pos, look_at, up); - } - // properties for light variables should be put here. - // not really hugely important yet but we will need them in the future - - light->set_color( - Color(assimp_light->mColorDiffuse.r, assimp_light->mColorDiffuse.g, assimp_light->mColorDiffuse.b)); - - return light; -} - -/** - * Create camera for the scene - */ -Node3D *EditorSceneImporterAssimp::create_camera( - ImportState &state, - const String &node_name, - Transform &look_at_transform) { - aiCamera *camera = state.assimp_scene->mCameras[state.camera_cache[node_name]]; - ERR_FAIL_COND_V(!camera, nullptr); - - Camera3D *camera_node = memnew(Camera3D); - ERR_FAIL_COND_V(!camera_node, nullptr); - float near = camera->mClipPlaneNear; - if (Math::is_equal_approx(near, 0.0f)) { - near = 0.1f; - } - camera_node->set_perspective(Math::rad2deg(camera->mHorizontalFOV) * 2.0f, near, camera->mClipPlaneFar); - Vector3 pos = Vector3(camera->mPosition.x, camera->mPosition.y, camera->mPosition.z); - Vector3 look_at = Vector3(camera->mLookAt.y, camera->mLookAt.x, camera->mLookAt.z).normalized(); - Vector3 up = Vector3(camera->mUp.x, camera->mUp.y, camera->mUp.z); - - look_at_transform.set_look_at(pos + look_at_transform.origin, look_at, up); - return camera_node; -} - -/** - * Generate node - * Recursive call to iterate over all nodes - */ -void EditorSceneImporterAssimp::_generate_node( - ImportState &state, - const aiNode *assimp_node) { - ERR_FAIL_COND(assimp_node == nullptr); - state.nodes.push_back(assimp_node); - String parent_name = AssimpUtils::get_assimp_string(assimp_node->mParent->mName); - - // please note - // duplicate bone names exist - // this is why we only check if the bone exists - // so everything else is useless but the name - // please do not copy any other values from get_bone_by_name. - aiBone *parent_bone = get_bone_by_name(state.assimp_scene, assimp_node->mParent->mName); - aiBone *current_bone = get_bone_by_name(state.assimp_scene, assimp_node->mName); - - // is this an armature - // parent null - // and this is the first bone :) - if (parent_bone == nullptr && current_bone) { - state.armature_nodes.push_back(assimp_node->mParent); - print_verbose("found valid armature: " + parent_name); - } - - for (size_t i = 0; i < assimp_node->mNumChildren; i++) { - _generate_node(state, assimp_node->mChildren[i]); - } -} diff --git a/modules/assimp/editor_scene_importer_assimp.h b/modules/assimp/editor_scene_importer_assimp.h deleted file mode 100644 index 80bba6ad66..0000000000 --- a/modules/assimp/editor_scene_importer_assimp.h +++ /dev/null @@ -1,149 +0,0 @@ -/*************************************************************************/ -/* editor_scene_importer_assimp.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 EDITOR_SCENE_IMPORTER_ASSIMP_H -#define EDITOR_SCENE_IMPORTER_ASSIMP_H - -#ifdef TOOLS_ENABLED -#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 <assimp/matrix4x4.h> -#include <assimp/scene.h> -#include <assimp/types.h> -#include <assimp/DefaultLogger.hpp> -#include <assimp/LogStream.hpp> -#include <assimp/Logger.hpp> -#include <map> - -#include "import_state.h" -#include "import_utils.h" - -using namespace AssimpImporter; - -class AssimpStream : public Assimp::LogStream { -public: - // Constructor - AssimpStream() {} - - // Destructor - ~AssimpStream() {} - // Write something using your own functionality - void write(const char *message) { - print_verbose(String("Open Asset Import: ") + String(message).strip_edges()); - } -}; - -class EditorSceneImporterAssimp : public EditorSceneImporter { -private: - GDCLASS(EditorSceneImporterAssimp, EditorSceneImporter); - - struct AssetImportAnimation { - enum Interpolation { - INTERP_LINEAR, - INTERP_STEP, - INTERP_CATMULLROMSPLINE, - INTERP_CUBIC_SPLINE - }; - }; - - struct BoneInfo { - uint32_t bone; - float weight; - }; - - Ref<Mesh> _generate_mesh_from_surface_indices(ImportState &state, const Vector<int> &p_surface_indices, - const aiNode *assimp_node, Ref<Skin> &skin, - Skeleton3D *&skeleton_assigned); - - // simple object creation functions - Node3D *create_light(ImportState &state, - const String &node_name, - Transform &look_at_transform); - Node3D *create_camera( - ImportState &state, - const String &node_name, - Transform &look_at_transform); - // non recursive - linear so must not use recursive arguments - MeshInstance3D *create_mesh(ImportState &state, const aiNode *assimp_node, const String &node_name, Node *active_node, Transform node_transform); - // recursive node generator - void _generate_node(ImportState &state, const aiNode *assimp_node); - void _insert_animation_track(ImportState &scene, const aiAnimation *assimp_anim, int track_id, - int anim_fps, Ref<Animation> animation, float ticks_per_second, - Skeleton3D *skeleton, const NodePath &node_path, - const String &node_name, aiBone *track_bone); - - void _import_animation(ImportState &state, int p_animation_index, int p_bake_fps); - Node *get_node_by_name(ImportState &state, String name); - aiBone *get_bone_from_stack(ImportState &state, aiString name); - Node3D *_generate_scene(const String &p_path, aiScene *scene, const uint32_t p_flags, int p_bake_fps, const int32_t p_max_bone_weights); - - template <class T> - T _interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time, AssetImportAnimation::Interpolation p_interp); - void _register_project_setting_import(const String generic, const String import_setting_string, const Vector<String> &exts, List<String> *r_extensions, const bool p_enabled) const; - - struct ImportFormat { - Vector<String> extensions; - bool is_default; - }; - -protected: - static void _bind_methods(); - -public: - EditorSceneImporterAssimp() { - Assimp::DefaultLogger::create("", Assimp::Logger::VERBOSE); - unsigned int severity = Assimp::Logger::Info | Assimp::Logger::Err | Assimp::Logger::Warn; - Assimp::DefaultLogger::get()->attachStream(new AssimpStream(), severity); - } - ~EditorSceneImporterAssimp() { - Assimp::DefaultLogger::kill(); - } - - virtual void get_extensions(List<String> *r_extensions) const override; - virtual uint32_t get_import_flags() const override; - virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = nullptr) override; - Ref<Image> load_image(ImportState &state, const aiScene *p_scene, String p_path); - - static void RegenerateBoneStack(ImportState &state); - - void RegenerateBoneStack(ImportState &state, aiMesh *mesh); -}; -#endif -#endif diff --git a/modules/assimp/godot_update_assimp.sh b/modules/assimp/godot_update_assimp.sh deleted file mode 100755 index ff8ff59e97..0000000000 --- a/modules/assimp/godot_update_assimp.sh +++ /dev/null @@ -1,262 +0,0 @@ -rm -rf ../../thirdparty/assimp -cd ../../thirdparty/ -git clone https://github.com/assimp/assimp.git -cd assimp -rm -rf code/3DSExporter.h -rm -rf code/3DSLoader.h -rm -rf code/3MFXmlTags.h -rm -rf code/ABCImporter.h -rm -rf code/ACLoader.h -rm -rf code/AMFImporter_Macro.hpp -rm -rf code/ASELoader.h -rm -rf code/assbin_chunks.h -rm -rf code/AssbinExporter.h -rm -rf code/AssbinLoader.h -rm -rf code/AssimpCExport.cpp -rm -rf code/AssxmlExporter.h -rm -rf code/B3DImporter.h -# rm -rf code/BaseProcess.cpp -# rm -rf code/BaseProcess.h -# rm -rf code/Bitmap.cpp -rm -rf code/BlenderBMesh.cpp -rm -rf code/BlenderBMesh.h -rm -rf code/BlenderCustomData.cpp -rm -rf code/BlenderCustomData.h -rm -rf code/BlenderIntermediate.h -rm -rf code/BlenderLoader.h -rm -rf code/BlenderModifier.h -rm -rf code/BlenderSceneGen.h -rm -rf code/BlenderTessellator.h -rm -rf code/BVHLoader.h -rm -rf code/C4DImporter.h -# rm -rf code/CalcTangentsProcess.h -# rm -rf code/CInterfaceIOWrapper.cpp -# rm -rf code/CInterfaceIOWrapper.h -rm -rf code/COBLoader.h -rm -rf code/COBScene.h -rm -rf code/ColladaExporter.h -rm -rf code/ColladaLoader.h -# rm -rf code/ComputeUVMappingProcess.h -# rm -rf code/ConvertToLHProcess.h -# rm -rf code/CreateAnimMesh.cpp -rm -rf code/CSMLoader.h -rm -rf code/D3MFExporter.h -rm -rf code/D3MFImporter.h -rm -rf code/D3MFOpcPackage.h -# rm -rf code/DeboneProcess.h -# rm -rf code/DefaultIOStream.cpp -# rm -rf code/DefaultIOSystem.cpp -# rm -rf code/DefaultProgressHandler.h -# rm -rf code/DropFaceNormalsProcess.cpp -# rm -rf code/DropFaceNormalsProcess.h -rm -rf code/DXFHelper.h -rm -rf code/DXFLoader.h -# rm -rf code/EmbedTexturesProcess.cpp -# rm -rf code/EmbedTexturesProcess.h -# rm -rf code/FBXCommon.h -# rm -rf code/FBXCompileConfig.h -# rm -rf code/FBXDeformer.cpp -# rm -rf code/FBXDocumentUtil.cpp -# rm -rf code/FBXDocumentUtil.h -# rm -rf code/FBXExporter.h -# rm -rf code/FBXExportNode.h -# rm -rf code/FBXExportProperty.h -# rm -rf code/FBXImporter.cpp -# rm -rf code/FBXImporter.h -# rm -rf code/FBXImportSettings.h -# rm -rf code/FBXMeshGeometry.h -# rm -rf code/FBXModel.cpp -# rm -rf code/FBXNodeAttribute.cpp -# rm -rf code/FBXParser.h -# rm -rf code/FBXProperties.cpp -# rm -rf code/FBXProperties.h -# rm -rf code/FBXTokenizer.cpp -# rm -rf code/FBXTokenizer.h -# rm -rf code/FBXUtil.cpp -# rm -rf code/FBXUtil.h -# rm -rf code/FileLogStream.h -# rm -rf code/FindDegenerates.h -# rm -rf code/FindInstancesProcess.h -# rm -rf code/FindInvalidDataProcess.h -rm -rf code/FIReader.hpp -# rm -rf code/FixNormalsStep.cpp -# rm -rf code/FixNormalsStep.h -# rm -rf code/GenFaceNormalsProcess.cpp -# rm -rf code/GenFaceNormalsProcess.h -# rm -rf code/GenVertexNormalsProcess.cpp -# rm -rf code/GenVertexNormalsProcess.h -rm -rf code/glTF2Asset.h -rm -rf code/glTF2Asset.inl -rm -rf code/glTF2AssetWriter.inl -rm -rf code/glTF2Exporter.cpp -rm -rf code/glTF2Importer.cpp -rm -rf code/glTF2AssetWriter.h -rm -rf code/glTFAsset.h -rm -rf code/glTFAsset.inl -rm -rf code/glTFAssetWriter.inl -rm -rf code/glTFExporter.cpp -rm -rf code/glTFImporter.cpp -rm -rf code/glTF2Exporter.h -rm -rf code/glTF2Importer.h -rm -rf code/glTFAssetWriter.h -rm -rf code/glTFExporter.h -rm -rf code/glTFImporter.h -rm -rf code/HalfLifeFileData.h -rm -rf code/HMPFileData.h -rm -rf code/HMPLoader.h -rm -rf code/HMPLoader.cpp -rm -rf code/IFF.h -# rm -rf code/Importer.h -# rm -rf code/ImproveCacheLocality.h -rm -rf code/IRRLoader.h -rm -rf code/IRRMeshLoader.h -rm -rf code/IRRShared.h -# rm -rf code/JoinVerticesProcess.h -# rm -rf code/LimitBoneWeightsProcess.cpp -# rm -rf code/LimitBoneWeightsProcess.h -rm -rf code/LWSLoader.h -rm -rf code/makefile.mingw -# rm -rf code/MakeVerboseFormat.cpp -# rm -rf code/MakeVerboseFormat.h -# rm -rf code/MaterialSystem.h -rm -rf code/MD2FileData.h -rm -rf code/MD2Loader.h -rm -rf code/MD2NormalTable.h -rm -rf code/MD3FileData.h -rm -rf code/MD3Loader.h -rm -rf code/MD4FileData.h -rm -rf code/MD5Loader.h -rm -rf code/MD5Parser.cpp -rm -rf code/MDCFileData.h -rm -rf code/MDCLoader.h -rm -rf code/MDLDefaultColorMap.h -# rm -rf code/MMDCpp14.h -# rm -rf code/MMDImporter.h -rm -rf code/MS3DLoader.h -rm -rf code/NDOLoader.h -rm -rf code/NFFLoader.h -rm -rf code/ObjExporter.h -rm -rf code/ObjFileImporter.h -rm -rf code/ObjFileMtlImporter.h -rm -rf code/ObjFileParser.h -rm -rf code/ObjTools.h -rm -rf code/ObjExporter.cpp -rm -rf code/ObjFileImporter.cpp -rm -rf code/ObjFileMtlImporter.cpp -rm -rf code/ObjFileParser.cpp -rm -rf code/OFFLoader.h -rm -rf code/OFFLoader.cpp -rm -rf code/OgreImporter.cpp -rm -rf code/OgreImporter.h -rm -rf code/OgreParsingUtils.h -rm -rf code/OgreXmlSerializer.h -rm -rf code/OgreXmlSerializer.cpp -rm -rf code/OgreBinarySerializer.cpp -rm -rf code/OpenGEXExporter.cpp -rm -rf code/OpenGEXExporter.h -rm -rf code/OpenGEXImporter.h -rm -rf code/OpenGEXStructs.h -rm -rf code/OpenGEXImporter.cpp -# rm -rf code/OptimizeGraph.h -# rm -rf code/OptimizeMeshes.cpp -# rm -rf code/OptimizeMeshes.h -rm -rf code/PlyExporter.h -rm -rf code/PlyLoader.h -# rm -rf code/PolyTools.h -# rm -rf code/PostStepRegistry.cpp -# rm -rf code/PretransformVertices.h -rm -rf code/Q3BSPFileData.h -rm -rf code/Q3BSPFileImporter.h -rm -rf code/Q3BSPFileParser.cpp -rm -rf code/Q3BSPFileParser.h -rm -rf code/Q3BSPZipArchive.cpp -rm -rf code/Q3BSPZipArchive.h -rm -rf code/Q3DLoader.h -rm -rf code/Q3DLoader.cpp -rm -rf code/Q3BSPFileImporter.cpp -rm -rf code/RawLoader.h -# rm -rf code/RemoveComments.cpp -# rm -rf code/RemoveRedundantMaterials.cpp -# rm -rf code/RemoveRedundantMaterials.h -# rm -rf code/RemoveVCProcess.h -# rm -rf code/ScaleProcess.cpp -# rm -rf code/ScaleProcess.h -# rm -rf code/scene.cpp -# rm -rf code/ScenePreprocessor.cpp -# rm -rf code/ScenePreprocessor.h -# rm -rf code/ScenePrivate.h -# rm -rf code/SGSpatialSort.cpp -rm -rf code/SIBImporter.h -rm -rf code/SMDLoader.cpp -# rm -rf code/simd.cpp -# rm -rf code/simd.h -# rm -rf code/SortByPTypeProcess.h -# rm -rf code/SplitByBoneCountProcess.h -# rm -rf code/SplitLargeMeshes.h -# rm -rf code/StdOStreamLogStream.h -rm -rf code/StepExporter.h -rm -rf code/StepExporter.cpp -rm -rf code/STLExporter.cpp -rm -rf code/STLExporter.h -rm -rf code/STLLoader.h -rm -rf code/STLLoader.cpp -# rm -rf code/TargetAnimation.cpp -# rm -rf code/TargetAnimation.h -rm -rf code/TerragenLoader.h -rm -rf code/TerragenLoader.cpp -# rm -rf code/TextureTransform.h -# rm -rf code/TriangulateProcess.h -rm -rf code/UnrealLoader.h -# rm -rf code/ValidateDataStructure.h -# rm -rf code/Version.cpp -# rm -rf code/VertexTriangleAdjacency.cpp -# rm -rf code/VertexTriangleAdjacency.h -# rm -rf code/Win32DebugLogStream.h -rm -rf code/X3DImporter_Macro.hpp -rm -rf code/X3DImporter_Metadata.cpp -rm -rf code/X3DImporter_Networking.cpp -rm -rf code/X3DImporter_Texturing.cpp -rm -rf code/X3DImporter_Shape.cpp -rm -rf code/X3DImporter_Rendering.cpp -rm -rf code/X3DImporter_Postprocess.cpp -rm -rf code/X3DImporter_Light.cpp -rm -rf code/X3DImporter_Group.cpp -rm -rf code/X3DImporter_Geometry3D.cpp -rm -rf code/X3DImporter_Geometry2D.cpp -rm -rf code/X3DImporter.cpp -rm -rf code/X3DExporter.cpp -rm -rf code/X3DVocabulary.cpp -rm -rf code/XFileExporter.h -rm -rf code/XFileExporter.cpp -rm -rf code/XFileHelper.h -rm -rf code/XFileHelper.cpp -rm -rf code/XFileImporter.h -rm -rf code/XFileImporter.cpp -rm -rf code/XFileParser.h -rm -rf code/XFileParser.cpp -rm -rf code/XGLLoader.h -rm -rf code/XGLLoader.cpp -rm -rf code/Importer -rm -rf .git -rm -rf cmake-modules -rm -rf doc -rm -rf packaging -rm -rf port -rm -rf samples -rm -rf scripts -rm -rf test -rm -rf tools -rm -rf contrib/zlib -rm -rf contrib/android-cmake -rm -rf contrib/gtest -rm -rf contrib/clipper -rm -rf contrib/irrXML -rm -rf contrib/Open3DGC -rm -rf contrib/openddlparser -rm -rf contrib/poly2tri -#rm -rf contrib/rapidjson -rm -rf contrib/unzip -rm -rf contrib/zip -rm -rf contrib/stb_image -rm .travis* - diff --git a/modules/assimp/import_state.h b/modules/assimp/import_state.h deleted file mode 100644 index a1cce6968b..0000000000 --- a/modules/assimp/import_state.h +++ /dev/null @@ -1,132 +0,0 @@ -/*************************************************************************/ -/* 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 EDITOR_SCENE_IMPORT_STATE_H -#define EDITOR_SCENE_IMPORT_STATE_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 <assimp/matrix4x4.h> -#include <assimp/scene.h> -#include <assimp/types.h> -#include <assimp/DefaultLogger.hpp> -#include <assimp/LogStream.hpp> -#include <assimp/Logger.hpp> - -namespace AssimpImporter { -/** Import state is for global scene import data - * This makes the code simpler and contains useful lookups. - */ -struct ImportState { - String path; - Node3D *root; - const aiScene *assimp_scene; - uint32_t max_bone_weights; - - Map<String, Ref<Mesh>> mesh_cache; - Map<int, Ref<Material>> material_cache; - Map<String, int> light_cache; - Map<String, int> camera_cache; - - // very useful for when you need to ask assimp for the bone mesh - - Map<const aiNode *, Node *> assimp_node_map; - Map<String, Ref<Image>> path_to_image_cache; - - // Generation 3 - determinisitic iteration - // to lower potential recursion errors - List<const aiNode *> nodes; - Map<const aiNode *, Node3D *> flat_node_map; - AnimationPlayer *animation_player; - - // Generation 3 - deterministic armatures - // list of armature nodes - flat and simple to parse - // assimp node, node in godot - List<aiNode *> armature_nodes; - Map<const aiNode *, Skeleton3D *> armature_skeletons; - Map<aiBone *, Skeleton3D *> skeleton_bone_map; - // Generation 3 - deterministic bone handling - // bones from the stack are popped when found - // this means we can detect - // what bones are for other armatures - List<aiBone *> bone_stack; - - // EditorSceneImporter::ImportFlags - uint32_t import_flags; -}; - -struct AssimpImageData { - Ref<Image> raw_image; - Ref<ImageTexture> texture; - aiTextureMapMode map_mode[2]; -}; - -/** Recursive state is used to push state into functions instead of specifying them - * This makes the code easier to handle too and add extra arguments without breaking things - */ -struct RecursiveState { - RecursiveState() {} // do not construct :) - RecursiveState( - Transform &_node_transform, - Skeleton3D *_skeleton, - Node3D *_new_node, - String &_node_name, - aiNode *_assimp_node, - Node *_parent_node, - aiBone *_bone) : - node_transform(_node_transform), - skeleton(_skeleton), - new_node(_new_node), - node_name(_node_name), - assimp_node(_assimp_node), - parent_node(_parent_node), - bone(_bone) {} - - Transform node_transform; - Skeleton3D *skeleton = nullptr; - Node3D *new_node = nullptr; - String node_name; - aiNode *assimp_node = nullptr; - Node *parent_node = nullptr; - aiBone *bone = nullptr; -}; -} // namespace AssimpImporter - -#endif // EDITOR_SCENE_IMPORT_STATE_H diff --git a/modules/assimp/import_utils.h b/modules/assimp/import_utils.h deleted file mode 100644 index dc85d06fed..0000000000 --- a/modules/assimp/import_utils.h +++ /dev/null @@ -1,463 +0,0 @@ -/*************************************************************************/ -/* import_utils.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_UTILS_IMPORTER_ASSIMP_H -#define IMPORT_UTILS_IMPORTER_ASSIMP_H - -#include "core/io/image_loader.h" -#include "import_state.h" - -#include <assimp/SceneCombiner.h> -#include <assimp/cexport.h> -#include <assimp/cimport.h> -#include <assimp/matrix4x4.h> -#include <assimp/pbrmaterial.h> -#include <assimp/postprocess.h> -#include <assimp/scene.h> -#include <assimp/DefaultLogger.hpp> -#include <assimp/Importer.hpp> -#include <assimp/LogStream.hpp> -#include <assimp/Logger.hpp> -#include <string> - -using namespace AssimpImporter; - -#define AI_PROPERTIES aiTextureType_UNKNOWN, 0 -#define AI_NULL 0, 0 -#define AI_MATKEY_FBX_MAYA_BASE_COLOR_FACTOR "$raw.Maya|baseColor" -#define AI_MATKEY_FBX_MAYA_METALNESS_FACTOR "$raw.Maya|metalness" -#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_FACTOR "$raw.Maya|diffuseRoughness" - -#define AI_MATKEY_FBX_MAYA_EMISSION_TEXTURE "$raw.Maya|emissionColor|file" -#define AI_MATKEY_FBX_MAYA_EMISSIVE_FACTOR "$raw.Maya|emission" -#define AI_MATKEY_FBX_MAYA_METALNESS_TEXTURE "$raw.Maya|metalness|file" -#define AI_MATKEY_FBX_MAYA_METALNESS_UV_XFORM "$raw.Maya|metalness|uvtrafo" -#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_TEXTURE "$raw.Maya|diffuseRoughness|file" -#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_UV_XFORM "$raw.Maya|diffuseRoughness|uvtrafo" -#define AI_MATKEY_FBX_MAYA_BASE_COLOR_TEXTURE "$raw.Maya|baseColor|file" -#define AI_MATKEY_FBX_MAYA_BASE_COLOR_UV_XFORM "$raw.Maya|baseColor|uvtrafo" -#define AI_MATKEY_FBX_MAYA_NORMAL_TEXTURE "$raw.Maya|normalCamera|file" -#define AI_MATKEY_FBX_MAYA_NORMAL_UV_XFORM "$raw.Maya|normalCamera|uvtrafo" - -#define AI_MATKEY_FBX_NORMAL_TEXTURE "$raw.Maya|normalCamera|file" -#define AI_MATKEY_FBX_NORMAL_UV_XFORM "$raw.Maya|normalCamera|uvtrafo" - -#define AI_MATKEY_FBX_MAYA_STINGRAY_DISPLACEMENT_SCALING_FACTOR "$raw.Maya|displacementscaling" -#define AI_MATKEY_FBX_MAYA_STINGRAY_BASE_COLOR_FACTOR "$raw.Maya|base_color" -#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_FACTOR "$raw.Maya|emissive" -#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_FACTOR "$raw.Maya|metallic" -#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_FACTOR "$raw.Maya|roughness" -#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_INTENSITY_FACTOR "$raw.Maya|emissive_intensity" - -#define AI_MATKEY_FBX_MAYA_STINGRAY_NORMAL_TEXTURE "$raw.Maya|TEX_normal_map|file" -#define AI_MATKEY_FBX_MAYA_STINGRAY_NORMAL_UV_XFORM "$raw.Maya|TEX_normal_map|uvtrafo" -#define AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_TEXTURE "$raw.Maya|TEX_color_map|file" -#define AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_UV_XFORM "$raw.Maya|TEX_color_map|uvtrafo" -#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_TEXTURE "$raw.Maya|TEX_metallic_map|file" -#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_UV_XFORM "$raw.Maya|TEX_metallic_map|uvtrafo" -#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_TEXTURE "$raw.Maya|TEX_roughness_map|file" -#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_UV_XFORM "$raw.Maya|TEX_roughness_map|uvtrafo" -#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_TEXTURE "$raw.Maya|TEX_emissive_map|file" -#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_UV_XFORM "$raw.Maya|TEX_emissive_map|uvtrafo" -#define AI_MATKEY_FBX_MAYA_STINGRAY_AO_TEXTURE "$raw.Maya|TEX_ao_map|file" -#define AI_MATKEY_FBX_MAYA_STINGRAY_AO_UV_XFORM "$raw.Maya|TEX_ao_map|uvtrafo" - -/** - * Assimp Utils - * Conversion tools / glue code to convert from assimp to godot -*/ -class AssimpUtils { -public: - /** - * calculate tangents for mesh data from assimp data - */ - static void calc_tangent_from_mesh(const aiMesh *ai_mesh, int i, int tri_index, int index, Color *w) { - const aiVector3D normals = ai_mesh->mAnimMeshes[i]->mNormals[tri_index]; - const Vector3 godot_normal = Vector3(normals.x, normals.y, normals.z); - const aiVector3D tangent = ai_mesh->mAnimMeshes[i]->mTangents[tri_index]; - const Vector3 godot_tangent = Vector3(tangent.x, tangent.y, tangent.z); - const aiVector3D bitangent = ai_mesh->mAnimMeshes[i]->mBitangents[tri_index]; - const Vector3 godot_bitangent = Vector3(bitangent.x, bitangent.y, bitangent.z); - float d = godot_normal.cross(godot_tangent).dot(godot_bitangent) > 0.0f ? 1.0f : -1.0f; - Color plane_tangent = Color(tangent.x, tangent.y, tangent.z, d); - w[index] = plane_tangent; - } - - struct AssetImportFbx { - enum ETimeMode { - TIME_MODE_DEFAULT = 0, - TIME_MODE_120 = 1, - TIME_MODE_100 = 2, - TIME_MODE_60 = 3, - TIME_MODE_50 = 4, - TIME_MODE_48 = 5, - TIME_MODE_30 = 6, - TIME_MODE_30_DROP = 7, - TIME_MODE_NTSC_DROP_FRAME = 8, - TIME_MODE_NTSC_FULL_FRAME = 9, - TIME_MODE_PAL = 10, - TIME_MODE_CINEMA = 11, - TIME_MODE_1000 = 12, - TIME_MODE_CINEMA_ND = 13, - TIME_MODE_CUSTOM = 14, - TIME_MODE_TIME_MODE_COUNT = 15 - }; - enum UpAxis { - UP_VECTOR_AXIS_X = 1, - UP_VECTOR_AXIS_Y = 2, - UP_VECTOR_AXIS_Z = 3 - }; - enum FrontAxis { - FRONT_PARITY_EVEN = 1, - FRONT_PARITY_ODD = 2, - }; - - enum CoordAxis { - COORD_RIGHT = 0, - COORD_LEFT = 1 - }; - }; - - /** Get assimp string - * automatically filters the string data - */ - static String get_assimp_string(const aiString &p_string) { - //convert an assimp String to a Godot String - String name; - name.parse_utf8(p_string.C_Str() /*,p_string.length*/); - if (name.find(":") != -1) { - String replaced_name = name.split(":")[1]; - print_verbose("Replacing " + name + " containing : with " + replaced_name); - name = replaced_name; - } - - return name; - } - - static String get_anim_string_from_assimp(const aiString &p_string) { - String name; - name.parse_utf8(p_string.C_Str() /*,p_string.length*/); - if (name.find(":") != -1) { - String replaced_name = name.split(":")[1]; - print_verbose("Replacing " + name + " containing : with " + replaced_name); - name = replaced_name; - } - return name; - } - - /** - * No filter logic get_raw_string_from_assimp - * This just convers the aiString to a parsed utf8 string - * Without removing special chars etc - */ - static String get_raw_string_from_assimp(const aiString &p_string) { - String name; - name.parse_utf8(p_string.C_Str() /*,p_string.length*/); - return name; - } - - static Ref<Animation> import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) { - return Ref<Animation>(); - } - - /** - * Converts aiMatrix4x4 to godot Transform - */ - static const Transform assimp_matrix_transform(const aiMatrix4x4 p_matrix) { - aiMatrix4x4 matrix = p_matrix; - Transform xform; - xform.set(matrix.a1, matrix.a2, matrix.a3, matrix.b1, matrix.b2, matrix.b3, matrix.c1, matrix.c2, matrix.c3, matrix.a4, matrix.b4, matrix.c4); - return xform; - } - - /** Get fbx fps for time mode meta data - */ - static float get_fbx_fps(int32_t time_mode, const aiScene *p_scene) { - switch (time_mode) { - case AssetImportFbx::TIME_MODE_DEFAULT: - return 24; //hack - case AssetImportFbx::TIME_MODE_120: - return 120; - case AssetImportFbx::TIME_MODE_100: - return 100; - case AssetImportFbx::TIME_MODE_60: - return 60; - case AssetImportFbx::TIME_MODE_50: - return 50; - case AssetImportFbx::TIME_MODE_48: - return 48; - case AssetImportFbx::TIME_MODE_30: - return 30; - case AssetImportFbx::TIME_MODE_30_DROP: - return 30; - case AssetImportFbx::TIME_MODE_NTSC_DROP_FRAME: - return 29.9700262f; - case AssetImportFbx::TIME_MODE_NTSC_FULL_FRAME: - return 29.9700262f; - case AssetImportFbx::TIME_MODE_PAL: - return 25; - case AssetImportFbx::TIME_MODE_CINEMA: - return 24; - case AssetImportFbx::TIME_MODE_1000: - return 1000; - case AssetImportFbx::TIME_MODE_CINEMA_ND: - return 23.976f; - case AssetImportFbx::TIME_MODE_CUSTOM: - int32_t frame_rate = -1; - p_scene->mMetaData->Get("FrameRate", frame_rate); - return frame_rate; - } - return 0; - } - - /** - * Get global transform for the current node - so we can use world space rather than - * local space coordinates - * useful if you need global - although recommend using local wherever possible over global - * as you could break fbx scaling :) - */ - static Transform _get_global_assimp_node_transform(const aiNode *p_current_node) { - aiNode const *current_node = p_current_node; - Transform xform; - while (current_node != nullptr) { - xform = assimp_matrix_transform(current_node->mTransformation) * xform; - current_node = current_node->mParent; - } - return xform; - } - - /** - * Find hardcoded textures from assimp which could be in many different directories - */ - static void find_texture_path(const String &p_path, _Directory &dir, String &path, bool &found, String extension) { - Vector<String> paths; - paths.push_back(path.get_basename() + extension); - paths.push_back(path + extension); - paths.push_back(path); - paths.push_back(p_path.get_base_dir().plus_file(path.get_file().get_basename() + extension)); - paths.push_back(p_path.get_base_dir().plus_file(path.get_file() + extension)); - paths.push_back(p_path.get_base_dir().plus_file(path.get_file())); - paths.push_back(p_path.get_base_dir().plus_file("textures/" + path.get_file().get_basename() + extension)); - paths.push_back(p_path.get_base_dir().plus_file("textures/" + path.get_file() + extension)); - paths.push_back(p_path.get_base_dir().plus_file("textures/" + path.get_file())); - paths.push_back(p_path.get_base_dir().plus_file("Textures/" + path.get_file().get_basename() + extension)); - paths.push_back(p_path.get_base_dir().plus_file("Textures/" + path.get_file() + extension)); - paths.push_back(p_path.get_base_dir().plus_file("Textures/" + path.get_file())); - paths.push_back(p_path.get_base_dir().plus_file("../Textures/" + path.get_file() + extension)); - paths.push_back(p_path.get_base_dir().plus_file("../Textures/" + path.get_file().get_basename() + extension)); - paths.push_back(p_path.get_base_dir().plus_file("../Textures/" + path.get_file())); - paths.push_back(p_path.get_base_dir().plus_file("../textures/" + path.get_file().get_basename() + extension)); - paths.push_back(p_path.get_base_dir().plus_file("../textures/" + path.get_file() + extension)); - paths.push_back(p_path.get_base_dir().plus_file("../textures/" + path.get_file())); - paths.push_back(p_path.get_base_dir().plus_file("texture/" + path.get_file().get_basename() + extension)); - paths.push_back(p_path.get_base_dir().plus_file("texture/" + path.get_file() + extension)); - paths.push_back(p_path.get_base_dir().plus_file("texture/" + path.get_file())); - paths.push_back(p_path.get_base_dir().plus_file("Texture/" + path.get_file().get_basename() + extension)); - paths.push_back(p_path.get_base_dir().plus_file("Texture/" + path.get_file() + extension)); - paths.push_back(p_path.get_base_dir().plus_file("Texture/" + path.get_file())); - paths.push_back(p_path.get_base_dir().plus_file("../Texture/" + path.get_file() + extension)); - paths.push_back(p_path.get_base_dir().plus_file("../Texture/" + path.get_file().get_basename() + extension)); - paths.push_back(p_path.get_base_dir().plus_file("../Texture/" + path.get_file())); - paths.push_back(p_path.get_base_dir().plus_file("../texture/" + path.get_file().get_basename() + extension)); - paths.push_back(p_path.get_base_dir().plus_file("../texture/" + path.get_file() + extension)); - paths.push_back(p_path.get_base_dir().plus_file("../texture/" + path.get_file())); - for (int i = 0; i < paths.size(); i++) { - if (dir.file_exists(paths[i])) { - found = true; - path = paths[i]; - return; - } - } - } - - /** find the texture path for the supplied fbx path inside godot - * very simple lookup for subfolders etc for a texture which may or may not be in a directory - */ - static void find_texture_path(const String &r_p_path, String &r_path, bool &r_found) { - _Directory dir; - - List<String> exts; - ImageLoader::get_recognized_extensions(&exts); - - Vector<String> split_path = r_path.get_basename().split("*"); - if (split_path.size() == 2) { - r_found = true; - return; - } - - if (dir.file_exists(r_p_path.get_base_dir() + r_path.get_file())) { - r_path = r_p_path.get_base_dir() + r_path.get_file(); - r_found = true; - return; - } - - for (int32_t i = 0; i < exts.size(); i++) { - if (r_found) { - return; - } - find_texture_path(r_p_path, dir, r_path, r_found, "." + exts[i]); - } - } - - /** - * set_texture_mapping_mode - * Helper to check the mapping mode of the texture (repeat, clamp and mirror) - */ - static void set_texture_mapping_mode(aiTextureMapMode *map_mode, Ref<ImageTexture> texture) { - ERR_FAIL_COND(texture.is_null()); - ERR_FAIL_COND(map_mode == nullptr); - // FIXME: Commented out during Vulkan port. - /* - aiTextureMapMode tex_mode = map_mode[0]; - - int32_t flags = Texture2D::FLAGS_DEFAULT; - if (tex_mode == aiTextureMapMode_Wrap) { - //Default - } else if (tex_mode == aiTextureMapMode_Clamp) { - flags = flags & ~Texture2D::FLAG_REPEAT; - } else if (tex_mode == aiTextureMapMode_Mirror) { - flags = flags | Texture2D::FLAG_MIRRORED_REPEAT; - } - texture->set_flags(flags); - */ - } - - /** - * Load or load from cache image :) - */ - static Ref<Image> load_image(ImportState &state, const aiScene *p_scene, String p_path) { - Map<String, Ref<Image>>::Element *match = state.path_to_image_cache.find(p_path); - - // if our cache contains this image then don't bother - if (match) { - return match->get(); - } - - Vector<String> split_path = p_path.get_basename().split("*"); - if (split_path.size() == 2) { - size_t texture_idx = split_path[1].to_int(); - ERR_FAIL_COND_V(texture_idx >= p_scene->mNumTextures, Ref<Image>()); - aiTexture *tex = p_scene->mTextures[texture_idx]; - String filename = AssimpUtils::get_raw_string_from_assimp(tex->mFilename); - filename = filename.get_file(); - print_verbose("Open Asset Import: Loading embedded texture " + filename); - if (tex->mHeight == 0) { - if (tex->CheckFormat("png")) { - ERR_FAIL_COND_V(Image::_png_mem_loader_func == nullptr, Ref<Image>()); - Ref<Image> img = Image::_png_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth); - ERR_FAIL_COND_V(img.is_null(), Ref<Image>()); - state.path_to_image_cache.insert(p_path, img); - return img; - } else if (tex->CheckFormat("jpg")) { - ERR_FAIL_COND_V(Image::_jpg_mem_loader_func == nullptr, Ref<Image>()); - Ref<Image> img = Image::_jpg_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth); - ERR_FAIL_COND_V(img.is_null(), Ref<Image>()); - state.path_to_image_cache.insert(p_path, img); - return img; - } else if (tex->CheckFormat("dds")) { - ERR_FAIL_COND_V_MSG(true, Ref<Image>(), "Open Asset Import: Embedded dds not implemented"); - } - } else { - Ref<Image> img; - img.instance(); - PackedByteArray arr; - uint32_t size = tex->mWidth * tex->mHeight; - arr.resize(size); - memcpy(arr.ptrw(), tex->pcData, size); - ERR_FAIL_COND_V(arr.size() % 4 != 0, Ref<Image>()); - //ARGB8888 to RGBA8888 - for (int32_t i = 0; i < arr.size() / 4; i++) { - arr.ptrw()[(4 * i) + 3] = arr[(4 * i) + 0]; - arr.ptrw()[(4 * i) + 0] = arr[(4 * i) + 1]; - arr.ptrw()[(4 * i) + 1] = arr[(4 * i) + 2]; - arr.ptrw()[(4 * i) + 2] = arr[(4 * i) + 3]; - } - img->create(tex->mWidth, tex->mHeight, true, Image::FORMAT_RGBA8, arr); - ERR_FAIL_COND_V(img.is_null(), Ref<Image>()); - state.path_to_image_cache.insert(p_path, img); - return img; - } - return Ref<Image>(); - } else { - Ref<Texture2D> texture = ResourceLoader::load(p_path); - ERR_FAIL_COND_V(texture.is_null(), Ref<Image>()); - Ref<Image> image = texture->get_data(); - ERR_FAIL_COND_V(image.is_null(), Ref<Image>()); - state.path_to_image_cache.insert(p_path, image); - return image; - } - - return Ref<Image>(); - } - - /* create texture from assimp data, if found in path */ - static bool CreateAssimpTexture( - AssimpImporter::ImportState &state, - aiString texture_path, - String &filename, - String &path, - AssimpImageData &image_state) { - filename = get_raw_string_from_assimp(texture_path); - path = state.path.get_base_dir().plus_file(filename.replace("\\", "/")); - bool found = false; - find_texture_path(state.path, path, found); - if (found) { - image_state.raw_image = AssimpUtils::load_image(state, state.assimp_scene, path); - if (image_state.raw_image.is_valid()) { - image_state.texture.instance(); - image_state.texture->create_from_image(image_state.raw_image); - // FIXME: Commented out during Vulkan port. - //image_state.texture->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY); - return true; - } - } - - return false; - } - /** GetAssimpTexture - * Designed to retrieve textures for you - */ - static bool GetAssimpTexture( - AssimpImporter::ImportState &state, - aiMaterial *ai_material, - aiTextureType texture_type, - String &filename, - String &path, - AssimpImageData &image_state) { - aiString ai_filename = aiString(); - if (AI_SUCCESS == ai_material->GetTexture(texture_type, 0, &ai_filename, nullptr, nullptr, nullptr, nullptr, image_state.map_mode)) { - return CreateAssimpTexture(state, ai_filename, filename, path, image_state); - } - - return false; - } -}; - -#endif // IMPORT_UTILS_IMPORTER_ASSIMP_H diff --git a/modules/basis_universal/SCsub b/modules/basis_universal/SCsub index dc7b176d24..351628a0e3 100644 --- a/modules/basis_universal/SCsub +++ b/modules/basis_universal/SCsub @@ -6,6 +6,9 @@ Import("env_modules") env_basisu = env_modules.Clone() # Thirdparty source files + +thirdparty_obj = [] + # Not unbundled so far since not widespread as shared library thirdparty_dir = "#thirdparty/basis_universal/" tool_sources = [ @@ -41,8 +44,16 @@ if env["target"] == "debug": env_thirdparty = env_basisu.Clone() env_thirdparty.disable_warnings() if env["tools"]: - env_thirdparty.add_source_files(env.modules_sources, tool_sources) -env_thirdparty.add_source_files(env.modules_sources, transcoder_sources) + env_thirdparty.add_source_files(thirdparty_obj, tool_sources) +env_thirdparty.add_source_files(thirdparty_obj, transcoder_sources) +env.modules_sources += thirdparty_obj # Godot source files -env_basisu.add_source_files(env.modules_sources, "*.cpp") + +module_obj = [] + +env_basisu.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj + +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/basis_universal/register_types.cpp b/modules/basis_universal/register_types.cpp index 27b299a65d..cf5581265b 100644 --- a/modules/basis_universal/register_types.cpp +++ b/modules/basis_universal/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -216,7 +216,7 @@ static Ref<Image> basis_universal_unpacker(const Vector<uint8_t> &p_buffer) { format = basist::transcoder_texture_format::cTFETC2; // get this from renderer imgfmt = Image::FORMAT_ETC2_RGBA8; } else { - //gles2 most likely, bad for normalmaps, nothing to do about this. + //gles2 most likely, bad for normal maps, nothing to do about this. format = basist::transcoder_texture_format::cTFRGBA32; imgfmt = Image::FORMAT_RGBA8; } diff --git a/modules/basis_universal/register_types.h b/modules/basis_universal/register_types.h index 5053dc27ce..30b465e344 100644 --- a/modules/basis_universal/register_types.h +++ b/modules/basis_universal/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/basis_universal/texture_basisu.cpp b/modules/basis_universal/texture_basisu.cpp index 5831d3de2a..66eb81d7f2 100644 --- a/modules/basis_universal/texture_basisu.cpp +++ b/modules/basis_universal/texture_basisu.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/basis_universal/texture_basisu.h b/modules/basis_universal/texture_basisu.h index 99248f9162..0a4783eaff 100644 --- a/modules/basis_universal/texture_basisu.h +++ b/modules/basis_universal/texture_basisu.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bmp/image_loader_bmp.cpp b/modules/bmp/image_loader_bmp.cpp index b08970d110..c7fdf56af4 100644 --- a/modules/bmp/image_loader_bmp.cpp +++ b/modules/bmp/image_loader_bmp.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bmp/image_loader_bmp.h b/modules/bmp/image_loader_bmp.h index 3f10a1c598..d3f12f0115 100644 --- a/modules/bmp/image_loader_bmp.h +++ b/modules/bmp/image_loader_bmp.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bmp/register_types.cpp b/modules/bmp/register_types.cpp index 6220e956d6..d36ce9cdaf 100644 --- a/modules/bmp/register_types.cpp +++ b/modules/bmp/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bmp/register_types.h b/modules/bmp/register_types.h index e7561dc32d..3ce81eba1b 100644 --- a/modules/bmp/register_types.h +++ b/modules/bmp/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/SCsub b/modules/bullet/SCsub index 21bdcca18e..bfac0df5b0 100644 --- a/modules/bullet/SCsub +++ b/modules/bullet/SCsub @@ -7,6 +7,8 @@ env_bullet = env_modules.Clone() # Thirdparty source files +thirdparty_obj = [] + if env["builtin_bullet"]: # Build only version 2 for now (as of 2.89) # Sync file list with relevant upstream CMakeLists.txt for each folder. @@ -208,8 +210,16 @@ if env["builtin_bullet"]: env_thirdparty = env_bullet.Clone() env_thirdparty.disable_warnings() - env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) + env.modules_sources += thirdparty_obj # Godot source files -env_bullet.add_source_files(env.modules_sources, "*.cpp") + +module_obj = [] + +env_bullet.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj + +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/bullet/area_bullet.cpp b/modules/bullet/area_bullet.cpp index f8f7d79a11..c3bd84c329 100644 --- a/modules/bullet/area_bullet.cpp +++ b/modules/bullet/area_bullet.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/area_bullet.h b/modules/bullet/area_bullet.h index 152fd785c0..a5fa678fec 100644 --- a/modules/bullet/area_bullet.h +++ b/modules/bullet/area_bullet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/btRayShape.cpp b/modules/bullet/btRayShape.cpp index a754ca6a89..1568cca63d 100644 --- a/modules/bullet/btRayShape.cpp +++ b/modules/bullet/btRayShape.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/btRayShape.h b/modules/bullet/btRayShape.h index d9ecde81e6..dcc4cc79c7 100644 --- a/modules/bullet/btRayShape.h +++ b/modules/bullet/btRayShape.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp index 663ad6e3e1..632682a15d 100644 --- a/modules/bullet/bullet_physics_server.cpp +++ b/modules/bullet/bullet_physics_server.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -1463,22 +1463,6 @@ bool BulletPhysicsServer3D::generic_6dof_joint_get_flag(RID p_joint, Vector3::Ax return generic_6dof_joint->get_flag(p_axis, p_flag); } -void BulletPhysicsServer3D::generic_6dof_joint_set_precision(RID p_joint, int p_precision) { - JointBullet *joint = joint_owner.getornull(p_joint); - ERR_FAIL_COND(!joint); - ERR_FAIL_COND(joint->get_type() != JOINT_6DOF); - Generic6DOFJointBullet *generic_6dof_joint = static_cast<Generic6DOFJointBullet *>(joint); - generic_6dof_joint->set_precision(p_precision); -} - -int BulletPhysicsServer3D::generic_6dof_joint_get_precision(RID p_joint) { - JointBullet *joint = joint_owner.getornull(p_joint); - ERR_FAIL_COND_V(!joint, 0); - ERR_FAIL_COND_V(joint->get_type() != JOINT_6DOF, 0); - Generic6DOFJointBullet *generic_6dof_joint = static_cast<Generic6DOFJointBullet *>(joint); - return generic_6dof_joint->get_precision(); -} - void BulletPhysicsServer3D::free(RID p_rid) { if (shape_owner.owns(p_rid)) { ShapeBullet *shape = shape_owner.getornull(p_rid); diff --git a/modules/bullet/bullet_physics_server.h b/modules/bullet/bullet_physics_server.h index dca9339c44..b5dc84c8f5 100644 --- a/modules/bullet/bullet_physics_server.h +++ b/modules/bullet/bullet_physics_server.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -376,9 +376,6 @@ public: virtual void generic_6dof_joint_set_flag(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisFlag p_flag, bool p_enable) override; virtual bool generic_6dof_joint_get_flag(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisFlag p_flag) override; - virtual void generic_6dof_joint_set_precision(RID p_joint, int precision) override; - virtual int generic_6dof_joint_get_precision(RID p_joint) override; - /* MISC */ virtual void free(RID p_rid) override; diff --git a/modules/bullet/bullet_types_converter.cpp b/modules/bullet/bullet_types_converter.cpp index 09b90fe09e..7b21e4e4b2 100644 --- a/modules/bullet/bullet_types_converter.cpp +++ b/modules/bullet/bullet_types_converter.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/bullet_types_converter.h b/modules/bullet/bullet_types_converter.h index fef07c55b7..ca9b7175dd 100644 --- a/modules/bullet/bullet_types_converter.h +++ b/modules/bullet/bullet_types_converter.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/bullet_utilities.h b/modules/bullet/bullet_utilities.h index a5e33d9829..a7c0fafbea 100644 --- a/modules/bullet/bullet_utilities.h +++ b/modules/bullet/bullet_utilities.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/collision_object_bullet.cpp b/modules/bullet/collision_object_bullet.cpp index a3158a15e5..bce8ec8076 100644 --- a/modules/bullet/collision_object_bullet.cpp +++ b/modules/bullet/collision_object_bullet.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/collision_object_bullet.h b/modules/bullet/collision_object_bullet.h index e2d05f2c38..bea28f2183 100644 --- a/modules/bullet/collision_object_bullet.h +++ b/modules/bullet/collision_object_bullet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/cone_twist_joint_bullet.cpp b/modules/bullet/cone_twist_joint_bullet.cpp index b4735fa9e9..e785780c5b 100644 --- a/modules/bullet/cone_twist_joint_bullet.cpp +++ b/modules/bullet/cone_twist_joint_bullet.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/cone_twist_joint_bullet.h b/modules/bullet/cone_twist_joint_bullet.h index ed4baa9d1b..7d6bafd292 100644 --- a/modules/bullet/cone_twist_joint_bullet.h +++ b/modules/bullet/cone_twist_joint_bullet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/constraint_bullet.cpp b/modules/bullet/constraint_bullet.cpp index c47a23e75f..e610727685 100644 --- a/modules/bullet/constraint_bullet.cpp +++ b/modules/bullet/constraint_bullet.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/constraint_bullet.h b/modules/bullet/constraint_bullet.h index 538808be51..6afd8c9b52 100644 --- a/modules/bullet/constraint_bullet.h +++ b/modules/bullet/constraint_bullet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/generic_6dof_joint_bullet.cpp b/modules/bullet/generic_6dof_joint_bullet.cpp index 56a66dba45..43ad6c56d5 100644 --- a/modules/bullet/generic_6dof_joint_bullet.cpp +++ b/modules/bullet/generic_6dof_joint_bullet.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -273,11 +273,3 @@ bool Generic6DOFJointBullet::get_flag(Vector3::Axis p_axis, PhysicsServer3D::G6D ERR_FAIL_INDEX_V(p_axis, 3, false); return flags[p_axis][p_flag]; } - -void Generic6DOFJointBullet::set_precision(int p_precision) { - sixDOFConstraint->setOverrideNumSolverIterations(MAX(1, p_precision)); -} - -int Generic6DOFJointBullet::get_precision() const { - return sixDOFConstraint->getOverrideNumSolverIterations(); -} diff --git a/modules/bullet/generic_6dof_joint_bullet.h b/modules/bullet/generic_6dof_joint_bullet.h index 316708bb11..62b8e85a81 100644 --- a/modules/bullet/generic_6dof_joint_bullet.h +++ b/modules/bullet/generic_6dof_joint_bullet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -68,9 +68,6 @@ public: void set_flag(Vector3::Axis p_axis, PhysicsServer3D::G6DOFJointAxisFlag p_flag, bool p_value); bool get_flag(Vector3::Axis p_axis, PhysicsServer3D::G6DOFJointAxisFlag p_flag) const; - - void set_precision(int p_precision); - int get_precision() const; }; #endif diff --git a/modules/bullet/godot_collision_configuration.cpp b/modules/bullet/godot_collision_configuration.cpp index ec7a1dbd9a..94f150b712 100644 --- a/modules/bullet/godot_collision_configuration.cpp +++ b/modules/bullet/godot_collision_configuration.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/godot_collision_configuration.h b/modules/bullet/godot_collision_configuration.h index ffad1b1bda..8ed55cb1da 100644 --- a/modules/bullet/godot_collision_configuration.h +++ b/modules/bullet/godot_collision_configuration.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/godot_collision_dispatcher.cpp b/modules/bullet/godot_collision_dispatcher.cpp index d919c85469..5d1e4d34d8 100644 --- a/modules/bullet/godot_collision_dispatcher.cpp +++ b/modules/bullet/godot_collision_dispatcher.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/godot_collision_dispatcher.h b/modules/bullet/godot_collision_dispatcher.h index 13e7255abf..77472a9432 100644 --- a/modules/bullet/godot_collision_dispatcher.h +++ b/modules/bullet/godot_collision_dispatcher.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/godot_motion_state.h b/modules/bullet/godot_motion_state.h index 90d1614a77..0669d2739a 100644 --- a/modules/bullet/godot_motion_state.h +++ b/modules/bullet/godot_motion_state.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/godot_ray_world_algorithm.cpp b/modules/bullet/godot_ray_world_algorithm.cpp index a84f3511ba..a8291d4ab4 100644 --- a/modules/bullet/godot_ray_world_algorithm.cpp +++ b/modules/bullet/godot_ray_world_algorithm.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/godot_ray_world_algorithm.h b/modules/bullet/godot_ray_world_algorithm.h index 9786732d40..f705edef81 100644 --- a/modules/bullet/godot_ray_world_algorithm.h +++ b/modules/bullet/godot_ray_world_algorithm.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/godot_result_callbacks.cpp b/modules/bullet/godot_result_callbacks.cpp index f82648d6ff..15d625afeb 100644 --- a/modules/bullet/godot_result_callbacks.cpp +++ b/modules/bullet/godot_result_callbacks.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/godot_result_callbacks.h b/modules/bullet/godot_result_callbacks.h index 1325542973..4f40f7ecfd 100644 --- a/modules/bullet/godot_result_callbacks.h +++ b/modules/bullet/godot_result_callbacks.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/hinge_joint_bullet.cpp b/modules/bullet/hinge_joint_bullet.cpp index 2338277565..4ceb98729f 100644 --- a/modules/bullet/hinge_joint_bullet.cpp +++ b/modules/bullet/hinge_joint_bullet.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/hinge_joint_bullet.h b/modules/bullet/hinge_joint_bullet.h index 120c40e5c0..06a95be374 100644 --- a/modules/bullet/hinge_joint_bullet.h +++ b/modules/bullet/hinge_joint_bullet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/joint_bullet.cpp b/modules/bullet/joint_bullet.cpp index 6257ff0058..ac371658f5 100644 --- a/modules/bullet/joint_bullet.cpp +++ b/modules/bullet/joint_bullet.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/joint_bullet.h b/modules/bullet/joint_bullet.h index c70cea817e..5bb8b50961 100644 --- a/modules/bullet/joint_bullet.h +++ b/modules/bullet/joint_bullet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/pin_joint_bullet.cpp b/modules/bullet/pin_joint_bullet.cpp index 1cfbc65c78..8e8ff57f11 100644 --- a/modules/bullet/pin_joint_bullet.cpp +++ b/modules/bullet/pin_joint_bullet.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/pin_joint_bullet.h b/modules/bullet/pin_joint_bullet.h index e7d05f34d4..6fbb6f7e02 100644 --- a/modules/bullet/pin_joint_bullet.h +++ b/modules/bullet/pin_joint_bullet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/register_types.cpp b/modules/bullet/register_types.cpp index d29b699ecd..b5ad5749a6 100644 --- a/modules/bullet/register_types.cpp +++ b/modules/bullet/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/register_types.h b/modules/bullet/register_types.h index 5a01a1422e..e405996705 100644 --- a/modules/bullet/register_types.h +++ b/modules/bullet/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/rid_bullet.h b/modules/bullet/rid_bullet.h index 0db09b2b78..0b74a0cc4d 100644 --- a/modules/bullet/rid_bullet.h +++ b/modules/bullet/rid_bullet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp index eb599df74c..7a53f91b33 100644 --- a/modules/bullet/rigid_body_bullet.cpp +++ b/modules/bullet/rigid_body_bullet.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -323,9 +323,6 @@ void RigidBodyBullet::set_space(SpaceBullet *p_space) { can_integrate_forces = false; isScratchedSpaceOverrideModificator = false; - // Remove all eventual constraints - assert_no_constraints(); - // Remove this object form the physics world space->remove_rigid_body(this); } @@ -443,12 +440,6 @@ bool RigidBodyBullet::was_colliding(RigidBodyBullet *p_other_object) { return false; } -void RigidBodyBullet::assert_no_constraints() { - if (btBody->getNumConstraintRefs()) { - WARN_PRINT("A body with a joints is destroyed. Please check the implementation in order to destroy the joint before the body."); - } -} - void RigidBodyBullet::set_activation_state(bool p_active) { if (p_active) { btBody->activate(); @@ -744,7 +735,7 @@ void RigidBodyBullet::set_continuous_collision_detection(bool p_enable) { } btBody->setCcdSweptSphereRadius(radius * 0.2); } else { - btBody->setCcdMotionThreshold(10000.0); + btBody->setCcdMotionThreshold(0.); btBody->setCcdSweptSphereRadius(0.); } } @@ -824,7 +815,7 @@ void RigidBodyBullet::reload_shapes() { btBody->updateInertiaTensor(); reload_kinematic_shapes(); - set_continuous_collision_detection(btBody->getCcdMotionThreshold() < 9998.0); + set_continuous_collision_detection(is_continuous_collision_detection_enabled()); reload_body(); } diff --git a/modules/bullet/rigid_body_bullet.h b/modules/bullet/rigid_body_bullet.h index c643611397..fc3f2db796 100644 --- a/modules/bullet/rigid_body_bullet.h +++ b/modules/bullet/rigid_body_bullet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -267,8 +267,6 @@ public: bool add_collision_object(RigidBodyBullet *p_otherObject, const Vector3 &p_hitWorldLocation, const Vector3 &p_hitLocalLocation, const Vector3 &p_hitNormal, const float &p_appliedImpulse, int p_other_shape_index, int p_local_shape_index); bool was_colliding(RigidBodyBullet *p_other_object); - void assert_no_constraints(); - void set_activation_state(bool p_active); bool is_active() const; diff --git a/modules/bullet/shape_bullet.cpp b/modules/bullet/shape_bullet.cpp index c7b761e92a..cc2ec28a9e 100644 --- a/modules/bullet/shape_bullet.cpp +++ b/modules/bullet/shape_bullet.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/shape_bullet.h b/modules/bullet/shape_bullet.h index 1c29dc1b1f..63475822de 100644 --- a/modules/bullet/shape_bullet.h +++ b/modules/bullet/shape_bullet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/shape_owner_bullet.cpp b/modules/bullet/shape_owner_bullet.cpp index d63096d9a3..ea8821eaec 100644 --- a/modules/bullet/shape_owner_bullet.cpp +++ b/modules/bullet/shape_owner_bullet.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/shape_owner_bullet.h b/modules/bullet/shape_owner_bullet.h index f909632c99..4bd583e096 100644 --- a/modules/bullet/shape_owner_bullet.h +++ b/modules/bullet/shape_owner_bullet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/slider_joint_bullet.cpp b/modules/bullet/slider_joint_bullet.cpp index 6d5d95d07a..45c892851b 100644 --- a/modules/bullet/slider_joint_bullet.cpp +++ b/modules/bullet/slider_joint_bullet.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/slider_joint_bullet.h b/modules/bullet/slider_joint_bullet.h index 6410b952ed..90964671c2 100644 --- a/modules/bullet/slider_joint_bullet.h +++ b/modules/bullet/slider_joint_bullet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/soft_body_bullet.cpp b/modules/bullet/soft_body_bullet.cpp index 6794d6c313..a490179964 100644 --- a/modules/bullet/soft_body_bullet.cpp +++ b/modules/bullet/soft_body_bullet.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/soft_body_bullet.h b/modules/bullet/soft_body_bullet.h index da8a2412ed..b15b72daf9 100644 --- a/modules/bullet/soft_body_bullet.h +++ b/modules/bullet/soft_body_bullet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp index abad1beacb..a8d55b59b3 100644 --- a/modules/bullet/space_bullet.cpp +++ b/modules/bullet/space_bullet.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -478,10 +478,20 @@ void SpaceBullet::add_rigid_body(RigidBodyBullet *p_body) { } void SpaceBullet::remove_rigid_body(RigidBodyBullet *p_body) { + btRigidBody *btBody = p_body->get_bt_rigid_body(); + + int constraints = btBody->getNumConstraintRefs(); + if (constraints > 0) { + ERR_PRINT("A body connected to joints was removed."); + for (int i = 0; i < constraints; i++) { + dynamicsWorld->removeConstraint(btBody->getConstraintRef(i)); + } + } + if (p_body->is_static()) { - dynamicsWorld->removeCollisionObject(p_body->get_bt_rigid_body()); + dynamicsWorld->removeCollisionObject(btBody); } else { - dynamicsWorld->removeRigidBody(p_body->get_bt_rigid_body()); + dynamicsWorld->removeRigidBody(btBody); } } @@ -828,17 +838,29 @@ void SpaceBullet::check_body_collision() { float appliedImpulse = pt.m_appliedImpulse; B_TO_G(pt.m_normalWorldOnB, normalOnB); + // The pt.m_index only contains the shape index when more than one collision shape is used + // and only if the collision shape is not a concave collision shape. + // A value of -1 in pt.m_partId indicates the pt.m_index is a shape index. + int shape_index_a = 0; + if (bodyA->get_shape_count() > 1 && pt.m_partId0 == -1) { + shape_index_a = pt.m_index0; + } + int shape_index_b = 0; + if (bodyB->get_shape_count() > 1 && pt.m_partId1 == -1) { + shape_index_b = pt.m_index1; + } + if (bodyA->can_add_collision()) { B_TO_G(pt.getPositionWorldOnB(), collisionWorldPosition); /// pt.m_localPointB Doesn't report the exact point in local space B_TO_G(pt.getPositionWorldOnB() - contactManifold->getBody1()->getWorldTransform().getOrigin(), collisionLocalPosition); - bodyA->add_collision_object(bodyB, collisionWorldPosition, collisionLocalPosition, normalOnB, appliedImpulse, pt.m_index1, pt.m_index0); + bodyA->add_collision_object(bodyB, collisionWorldPosition, collisionLocalPosition, normalOnB, appliedImpulse, shape_index_b, shape_index_a); } if (bodyB->can_add_collision()) { B_TO_G(pt.getPositionWorldOnA(), collisionWorldPosition); /// pt.m_localPointA Doesn't report the exact point in local space B_TO_G(pt.getPositionWorldOnA() - contactManifold->getBody0()->getWorldTransform().getOrigin(), collisionLocalPosition); - bodyB->add_collision_object(bodyA, collisionWorldPosition, collisionLocalPosition, normalOnB * -1, appliedImpulse * -1, pt.m_index0, pt.m_index1); + bodyB->add_collision_object(bodyA, collisionWorldPosition, collisionLocalPosition, normalOnB * -1, appliedImpulse * -1, shape_index_a, shape_index_b); } #ifdef DEBUG_ENABLED diff --git a/modules/bullet/space_bullet.h b/modules/bullet/space_bullet.h index e362f27d39..42f982d5f0 100644 --- a/modules/bullet/space_bullet.h +++ b/modules/bullet/space_bullet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -167,7 +167,7 @@ public: BulletPhysicsDirectSpaceState *get_direct_state(); void set_debug_contacts(int p_amount) { contactDebug.resize(p_amount); } - _FORCE_INLINE_ bool is_debugging_contacts() const { return !contactDebug.empty(); } + _FORCE_INLINE_ bool is_debugging_contacts() const { return !contactDebug.is_empty(); } _FORCE_INLINE_ void reset_debug_contact_count() { contactDebugCount = 0; } diff --git a/modules/camera/camera_osx.h b/modules/camera/camera_osx.h index a07b83c6af..964b7c1edc 100644 --- a/modules/camera/camera_osx.h +++ b/modules/camera/camera_osx.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/camera/camera_osx.mm b/modules/camera/camera_osx.mm index 306632a016..3d2053ad23 100644 --- a/modules/camera/camera_osx.mm +++ b/modules/camera/camera_osx.mm @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/camera/camera_win.cpp b/modules/camera/camera_win.cpp index 1646644be3..226a642dcf 100644 --- a/modules/camera/camera_win.cpp +++ b/modules/camera/camera_win.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/camera/camera_win.h b/modules/camera/camera_win.h index bbc8880c12..671e7d5beb 100644 --- a/modules/camera/camera_win.h +++ b/modules/camera/camera_win.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/camera/register_types.cpp b/modules/camera/register_types.cpp index 9479310a13..0d33ff9ddc 100644 --- a/modules/camera/register_types.cpp +++ b/modules/camera/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/camera/register_types.h b/modules/camera/register_types.h index e34f84bf2c..0ae9aa2c0b 100644 --- a/modules/camera/register_types.h +++ b/modules/camera/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/camera_iphone/camera_ios.h b/modules/camera_iphone/camera_ios.h index 7da43e4851..0566457a0f 100644 --- a/modules/camera_iphone/camera_ios.h +++ b/modules/camera_iphone/camera_ios.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/camera_iphone/camera_ios.mm b/modules/camera_iphone/camera_ios.mm index e4cb928805..39568fbd6c 100644 --- a/modules/camera_iphone/camera_ios.mm +++ b/modules/camera_iphone/camera_ios.mm @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/camera_iphone/camera_module.cpp b/modules/camera_iphone/camera_module.cpp index f3d00be204..7ea035892e 100644 --- a/modules/camera_iphone/camera_module.cpp +++ b/modules/camera_iphone/camera_module.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/camera_iphone/camera_module.h b/modules/camera_iphone/camera_module.h index d123071a70..5a94d8b529 100644 --- a/modules/camera_iphone/camera_module.h +++ b/modules/camera_iphone/camera_module.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp index 04e1c4de35..f0a2f17ba9 100644 --- a/modules/csg/csg.cpp +++ b/modules/csg/csg.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/csg/csg.h b/modules/csg/csg.h index ef1103e1ac..1612c16a32 100644 --- a/modules/csg/csg.h +++ b/modules/csg/csg.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/csg/csg_gizmos.cpp b/modules/csg/csg_gizmos.cpp index cce72770f5..e23442ef99 100644 --- a/modules/csg/csg_gizmos.cpp +++ b/modules/csg/csg_gizmos.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -56,8 +56,7 @@ String CSGShape3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, } if (Object::cast_to<CSGBox3D>(cs)) { - static const char *hname[3] = { "Width", "Height", "Depth" }; - return hname[p_idx]; + return "Size"; } if (Object::cast_to<CSGCylinder3D>(cs)) { @@ -81,14 +80,7 @@ Variant CSGShape3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int if (Object::cast_to<CSGBox3D>(cs)) { CSGBox3D *s = Object::cast_to<CSGBox3D>(cs); - switch (p_idx) { - case 0: - return s->get_width(); - case 1: - return s->get_height(); - case 2: - return s->get_depth(); - } + return s->get_size(); } if (Object::cast_to<CSGCylinder3D>(cs)) { @@ -123,7 +115,7 @@ void CSGShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Ca Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb); float d = ra.x; if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); + d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); } if (d < 0.001) { @@ -142,24 +134,16 @@ void CSGShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Ca Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); float d = ra[p_idx]; if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); + d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); } if (d < 0.001) { d = 0.001; } - switch (p_idx) { - case 0: - s->set_width(d * 2); - break; - case 1: - s->set_height(d * 2); - break; - case 2: - s->set_depth(d * 2); - break; - } + Vector3 h = s->get_size(); + h[p_idx] = d * 2; + s->set_size(h); } if (Object::cast_to<CSGCylinder3D>(cs)) { @@ -171,7 +155,7 @@ void CSGShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Ca Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); float d = axis.dot(ra); if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); + d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); } if (d < 0.001) { @@ -194,7 +178,7 @@ void CSGShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Ca Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); float d = axis.dot(ra); if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap()); + d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); } if (d < 0.001) { @@ -229,38 +213,14 @@ void CSGShape3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, if (Object::cast_to<CSGBox3D>(cs)) { CSGBox3D *s = Object::cast_to<CSGBox3D>(cs); if (p_cancel) { - switch (p_idx) { - case 0: - s->set_width(p_restore); - break; - case 1: - s->set_height(p_restore); - break; - case 2: - s->set_depth(p_restore); - break; - } + s->set_size(p_restore); return; } UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Box Shape Extents")); - static const char *method[3] = { "set_width", "set_height", "set_depth" }; - float current = 0; - switch (p_idx) { - case 0: - current = s->get_width(); - break; - case 1: - current = s->get_height(); - break; - case 2: - current = s->get_depth(); - break; - } - - ur->add_do_method(s, method[p_idx], current); - ur->add_undo_method(s, method[p_idx], p_restore); + ur->create_action(TTR("Change Box Shape Size")); + ur->add_do_method(s, "set_size", s->get_size()); + ur->add_undo_method(s, "set_size", p_restore); ur->commit_action(); } @@ -319,7 +279,7 @@ bool CSGShape3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { return Object::cast_to<CSGSphere3D>(p_spatial) || Object::cast_to<CSGBox3D>(p_spatial) || Object::cast_to<CSGCylinder3D>(p_spatial) || Object::cast_to<CSGTorus3D>(p_spatial) || Object::cast_to<CSGMesh3D>(p_spatial) || Object::cast_to<CSGPolygon3D>(p_spatial); } -String CSGShape3DGizmoPlugin::get_name() const { +String CSGShape3DGizmoPlugin::get_gizmo_name() const { return "CSGShape3D"; } @@ -408,9 +368,13 @@ void CSGShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { CSGBox3D *s = Object::cast_to<CSGBox3D>(cs); Vector<Vector3> handles; - handles.push_back(Vector3(s->get_width() * 0.5, 0, 0)); - handles.push_back(Vector3(0, s->get_height() * 0.5, 0)); - handles.push_back(Vector3(0, 0, s->get_depth() * 0.5)); + + for (int i = 0; i < 3; i++) { + Vector3 h; + h[i] = s->get_size()[i] / 2; + handles.push_back(h); + } + p_gizmo->add_handles(handles, handles_material); } diff --git a/modules/csg/csg_gizmos.h b/modules/csg/csg_gizmos.h index 83ee847caf..8f7da35de3 100644 --- a/modules/csg/csg_gizmos.h +++ b/modules/csg/csg_gizmos.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -40,7 +40,7 @@ class CSGShape3DGizmoPlugin : public EditorNode3DGizmoPlugin { public: bool has_gizmo(Node3D *p_spatial) override; - String get_name() const override; + String get_gizmo_name() const override; int get_priority() const override; bool is_selectable_when_hidden() const override; void redraw(EditorNode3DGizmo *p_gizmo) override; diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index 8f2ebc7232..042c3aaca7 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -701,7 +701,7 @@ CSGPrimitive3D::CSGPrimitive3D() { CSGBrush *CSGMesh3D::_build_brush() { if (!mesh.is_valid()) { - return nullptr; + return memnew(CSGBrush); } Vector<Vector3> vertices; @@ -719,7 +719,7 @@ CSGBrush *CSGMesh3D::_build_brush() { if (arrays.size() == 0) { _make_dirty(); - ERR_FAIL_COND_V(arrays.size() == 0, nullptr); + ERR_FAIL_COND_V(arrays.size() == 0, memnew(CSGBrush)); } Vector<Vector3> avertices = arrays[Mesh::ARRAY_VERTEX]; @@ -840,7 +840,7 @@ CSGBrush *CSGMesh3D::_build_brush() { } if (vertices.size() == 0) { - return nullptr; + return memnew(CSGBrush); } return _create_brush_from_arrays(vertices, uvs, smooth, materials); @@ -1125,7 +1125,7 @@ CSGBrush *CSGBox3D::_build_brush() { int face = 0; - Vector3 vertex_mul(width * 0.5, height * 0.5, depth * 0.5); + Vector3 vertex_mul = size / 2; { for (int i = 0; i < 6; i++) { @@ -1194,55 +1194,25 @@ CSGBrush *CSGBox3D::_build_brush() { } void CSGBox3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_width", "width"), &CSGBox3D::set_width); - ClassDB::bind_method(D_METHOD("get_width"), &CSGBox3D::get_width); - - ClassDB::bind_method(D_METHOD("set_height", "height"), &CSGBox3D::set_height); - ClassDB::bind_method(D_METHOD("get_height"), &CSGBox3D::get_height); - - ClassDB::bind_method(D_METHOD("set_depth", "depth"), &CSGBox3D::set_depth); - ClassDB::bind_method(D_METHOD("get_depth"), &CSGBox3D::get_depth); + ClassDB::bind_method(D_METHOD("set_size", "size"), &CSGBox3D::set_size); + ClassDB::bind_method(D_METHOD("get_size"), &CSGBox3D::get_size); ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGBox3D::set_material); ClassDB::bind_method(D_METHOD("get_material"), &CSGBox3D::get_material); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_width", "get_width"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_height", "get_height"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_depth", "get_depth"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size"), "set_size", "get_size"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "StandardMaterial3D,ShaderMaterial"), "set_material", "get_material"); } -void CSGBox3D::set_width(const float p_width) { - width = p_width; - _make_dirty(); - update_gizmo(); - _change_notify("width"); -} - -float CSGBox3D::get_width() const { - return width; -} - -void CSGBox3D::set_height(const float p_height) { - height = p_height; - _make_dirty(); - update_gizmo(); - _change_notify("height"); -} - -float CSGBox3D::get_height() const { - return height; -} - -void CSGBox3D::set_depth(const float p_depth) { - depth = p_depth; +void CSGBox3D::set_size(const Vector3 &p_size) { + size = p_size; _make_dirty(); update_gizmo(); - _change_notify("depth"); + _change_notify("size"); } -float CSGBox3D::get_depth() const { - return depth; +Vector3 CSGBox3D::get_size() const { + return size; } void CSGBox3D::set_material(const Ref<Material> &p_material) { @@ -1255,13 +1225,6 @@ Ref<Material> CSGBox3D::get_material() const { return material; } -CSGBox3D::CSGBox3D() { - // defaults - width = 2.0; - height = 2.0; - depth = 2.0; -} - /////////////// CSGBrush *CSGCylinder3D::_build_brush() { @@ -1502,7 +1465,7 @@ CSGBrush *CSGTorus3D::_build_brush() { float max_radius = outer_radius; if (min_radius == max_radius) { - return nullptr; //sorry, can't + return memnew(CSGBrush); //sorry, can't } if (min_radius > max_radius) { @@ -1721,7 +1684,7 @@ CSGBrush *CSGPolygon3D::_build_brush() { // set our bounding box if (polygon.size() < 3) { - return nullptr; + return memnew(CSGBrush); } Vector<Point2> final_polygon = polygon; @@ -1733,7 +1696,7 @@ CSGBrush *CSGPolygon3D::_build_brush() { Vector<int> triangles = Geometry2D::triangulate_polygon(final_polygon); if (triangles.size() < 3) { - return nullptr; + return memnew(CSGBrush); } Path3D *path = nullptr; @@ -1767,15 +1730,15 @@ CSGBrush *CSGPolygon3D::_build_brush() { if (mode == MODE_PATH) { if (!has_node(path_node)) { - return nullptr; + return memnew(CSGBrush); } Node *n = get_node(path_node); if (!n) { - return nullptr; + return memnew(CSGBrush); } path = Object::cast_to<Path3D>(n); if (!path) { - return nullptr; + return memnew(CSGBrush); } if (path != path_cache) { @@ -1793,10 +1756,10 @@ CSGBrush *CSGPolygon3D::_build_brush() { } curve = path->get_curve(); if (curve.is_null()) { - return nullptr; + return memnew(CSGBrush); } if (curve->get_baked_length() <= 0) { - return nullptr; + return memnew(CSGBrush); } } CSGBrush *brush = memnew(CSGBrush); diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h index d93693f145..7dff8b6d3b 100644 --- a/modules/csg/csg_shape.h +++ b/modules/csg/csg_shape.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -240,27 +240,19 @@ class CSGBox3D : public CSGPrimitive3D { virtual CSGBrush *_build_brush() override; Ref<Material> material; - float width; - float height; - float depth; + Vector3 size = Vector3(2, 2, 2); protected: static void _bind_methods(); public: - void set_width(const float p_width); - float get_width() const; - - void set_height(const float p_height); - float get_height() const; - - void set_depth(const float p_depth); - float get_depth() const; + void set_size(const Vector3 &p_size); + Vector3 get_size() const; void set_material(const Ref<Material> &p_material); Ref<Material> get_material() const; - CSGBox3D(); + CSGBox3D() {} }; class CSGCylinder3D : public CSGPrimitive3D { diff --git a/modules/csg/doc_classes/CSGBox3D.xml b/modules/csg/doc_classes/CSGBox3D.xml index 492bf68c44..b1d0454b76 100644 --- a/modules/csg/doc_classes/CSGBox3D.xml +++ b/modules/csg/doc_classes/CSGBox3D.xml @@ -11,17 +11,11 @@ <methods> </methods> <members> - <member name="depth" type="float" setter="set_depth" getter="get_depth" default="2.0"> - Depth of the box measured from the center of the box. - </member> - <member name="height" type="float" setter="set_height" getter="get_height" default="2.0"> - Height of the box measured from the center of the box. - </member> <member name="material" type="Material" setter="set_material" getter="get_material"> The material used to render the box. </member> - <member name="width" type="float" setter="set_width" getter="get_width" default="2.0"> - Width of the box measured from the center of the box. + <member name="size" type="Vector3" setter="set_size" getter="get_size" default="Vector3( 2, 2, 2 )"> + The box's width, height and depth. </member> </members> <constants> diff --git a/modules/csg/register_types.cpp b/modules/csg/register_types.cpp index a8bcc2fed1..e28f44d1eb 100644 --- a/modules/csg/register_types.cpp +++ b/modules/csg/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/csg/register_types.h b/modules/csg/register_types.h index 926e598561..8747b3fade 100644 --- a/modules/csg/register_types.h +++ b/modules/csg/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/cvtt/SCsub b/modules/cvtt/SCsub index 5438f7ebac..e56177d6e9 100644 --- a/modules/cvtt/SCsub +++ b/modules/cvtt/SCsub @@ -6,6 +6,9 @@ Import("env_modules") env_cvtt = env_modules.Clone() # Thirdparty source files + +thirdparty_obj = [] + thirdparty_dir = "#thirdparty/cvtt/" thirdparty_sources = [ "ConvectionKernels.cpp", @@ -17,7 +20,15 @@ env_cvtt.Prepend(CPPPATH=[thirdparty_dir]) env_thirdparty = env_cvtt.Clone() env_thirdparty.disable_warnings() -env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) +env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) +env.modules_sources += thirdparty_obj # Godot source files -env_cvtt.add_source_files(env.modules_sources, "*.cpp") + +module_obj = [] + +env_cvtt.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj + +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/cvtt/image_compress_cvtt.cpp b/modules/cvtt/image_compress_cvtt.cpp index 5d97164dbf..6661dbbb0b 100644 --- a/modules/cvtt/image_compress_cvtt.cpp +++ b/modules/cvtt/image_compress_cvtt.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -168,9 +168,10 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChann flags |= cvtt::Flags::BC7_RespectPunchThrough; - if (p_channels == Image::USED_CHANNELS_RG) { //guessing this is a normalmap + if (p_channels == Image::USED_CHANNELS_RG) { //guessing this is a normal map flags |= cvtt::Flags::Uniform; } + options.flags = flags; Image::Format target_format = Image::FORMAT_BPTC_RGBA; diff --git a/modules/cvtt/image_compress_cvtt.h b/modules/cvtt/image_compress_cvtt.h index 483fb876a6..bef5653fa9 100644 --- a/modules/cvtt/image_compress_cvtt.h +++ b/modules/cvtt/image_compress_cvtt.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/cvtt/register_types.cpp b/modules/cvtt/register_types.cpp index e4a01cc787..055b5dc6e3 100644 --- a/modules/cvtt/register_types.cpp +++ b/modules/cvtt/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/cvtt/register_types.h b/modules/cvtt/register_types.h index 36b5e332d6..e62e8c0e9a 100644 --- a/modules/cvtt/register_types.h +++ b/modules/cvtt/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/dds/register_types.cpp b/modules/dds/register_types.cpp index 3991964a28..1444d33171 100644 --- a/modules/dds/register_types.cpp +++ b/modules/dds/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/dds/register_types.h b/modules/dds/register_types.h index 3cb7b5c2a6..b84bcd06c8 100644 --- a/modules/dds/register_types.h +++ b/modules/dds/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp index 627153fbc8..2865b3c9ae 100644 --- a/modules/dds/texture_loader_dds.cpp +++ b/modules/dds/texture_loader_dds.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/dds/texture_loader_dds.h b/modules/dds/texture_loader_dds.h index ef08967df7..605e791969 100644 --- a/modules/dds/texture_loader_dds.h +++ b/modules/dds/texture_loader_dds.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/denoise/SCsub b/modules/denoise/SCsub index bf3bd7d073..97feea2b44 100644 --- a/modules/denoise/SCsub +++ b/modules/denoise/SCsub @@ -8,6 +8,9 @@ Import("env_modules") env_oidn = env_modules.Clone() # Thirdparty source files + +thirdparty_obj = [] + thirdparty_dir = "#thirdparty/oidn/" thirdparty_sources = [ "core/api.cpp", @@ -106,7 +109,8 @@ env_oidn.Append( env_thirdparty = env_oidn.Clone() env_thirdparty.disable_warnings() -env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) +env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) +env.modules_sources += thirdparty_obj weights_in_path = thirdparty_dir + "weights/rtlightmap_hdr.tza" weights_out_path = thirdparty_dir + "weights/rtlightmap_hdr.gen.cpp" @@ -114,5 +118,12 @@ weights_out_path = thirdparty_dir + "weights/rtlightmap_hdr.gen.cpp" env_thirdparty.Depends(weights_out_path, weights_in_path) env_thirdparty.CommandNoCache(weights_out_path, weights_in_path, resource_to_cpp.tza_to_cpp) -env_oidn.add_source_files(env.modules_sources, "denoise_wrapper.cpp") -env_modules.add_source_files(env.modules_sources, ["register_types.cpp", "lightmap_denoiser.cpp"]) +# Godot source files + +module_obj = [] + +env_oidn.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj + +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/denoise/denoise_wrapper.cpp b/modules/denoise/denoise_wrapper.cpp index c12c6d9c31..e71fce5958 100644 --- a/modules/denoise/denoise_wrapper.cpp +++ b/modules/denoise/denoise_wrapper.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/denoise/denoise_wrapper.h b/modules/denoise/denoise_wrapper.h index 2107df09c1..25e342bc93 100644 --- a/modules/denoise/denoise_wrapper.h +++ b/modules/denoise/denoise_wrapper.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/denoise/lightmap_denoiser.cpp b/modules/denoise/lightmap_denoiser.cpp index 29d02e8ee2..003bc832b0 100644 --- a/modules/denoise/lightmap_denoiser.cpp +++ b/modules/denoise/lightmap_denoiser.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/denoise/lightmap_denoiser.h b/modules/denoise/lightmap_denoiser.h index f4e4335d9b..f1992a1733 100644 --- a/modules/denoise/lightmap_denoiser.h +++ b/modules/denoise/lightmap_denoiser.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/denoise/register_types.cpp b/modules/denoise/register_types.cpp index 552495ed87..936e5f604d 100644 --- a/modules/denoise/register_types.cpp +++ b/modules/denoise/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/denoise/register_types.h b/modules/denoise/register_types.h index f0f1f44bfe..516a91b134 100644 --- a/modules/denoise/register_types.h +++ b/modules/denoise/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/enet/SCsub b/modules/enet/SCsub index c8f4b3885e..580e5a3eb0 100644 --- a/modules/enet/SCsub +++ b/modules/enet/SCsub @@ -7,6 +7,8 @@ env_enet = env_modules.Clone() # Thirdparty source files +thirdparty_obj = [] + if env["builtin_enet"]: thirdparty_dir = "#thirdparty/enet/" thirdparty_sources = [ @@ -26,6 +28,16 @@ if env["builtin_enet"]: env_thirdparty = env_enet.Clone() env_thirdparty.disable_warnings() - env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) + env.modules_sources += thirdparty_obj + + +# Godot source files + +module_obj = [] + +env_enet.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj -env_enet.add_source_files(env.modules_sources, "*.cpp") +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp index 64977ad237..66db9ab84e 100644 --- a/modules/enet/networked_multiplayer_enet.cpp +++ b/modules/enet/networked_multiplayer_enet.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/enet/networked_multiplayer_enet.h b/modules/enet/networked_multiplayer_enet.h index 722c7001fd..4baa48be5e 100644 --- a/modules/enet/networked_multiplayer_enet.h +++ b/modules/enet/networked_multiplayer_enet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/enet/register_types.cpp b/modules/enet/register_types.cpp index 2683f3155b..8da2d17e13 100644 --- a/modules/enet/register_types.cpp +++ b/modules/enet/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/enet/register_types.h b/modules/enet/register_types.h index cac0a4f7ee..75f4cba61b 100644 --- a/modules/enet/register_types.h +++ b/modules/enet/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/etc/SCsub b/modules/etc/SCsub index 383bbf83c3..9b46f17916 100644 --- a/modules/etc/SCsub +++ b/modules/etc/SCsub @@ -6,6 +6,9 @@ Import("env_modules") env_etc = env_modules.Clone() # Thirdparty source files + +thirdparty_obj = [] + # Not unbundled so far since not widespread as shared library thirdparty_dir = "#thirdparty/etc2comp/" thirdparty_sources = [ @@ -31,7 +34,15 @@ env_etc.Prepend(CPPPATH=[thirdparty_dir]) env_thirdparty = env_etc.Clone() env_thirdparty.disable_warnings() -env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) +env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) +env.modules_sources += thirdparty_obj # Godot source files -env_etc.add_source_files(env.modules_sources, "*.cpp") + +module_obj = [] + +env_etc.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj + +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/etc/image_etc.cpp b/modules/etc/image_compress_etc.cpp index 4e9e64c6a7..41cbbe3f54 100644 --- a/modules/etc/image_etc.cpp +++ b/modules/etc/image_compress_etc.cpp @@ -1,12 +1,12 @@ /*************************************************************************/ -/* image_etc.cpp */ +/* image_compress_etc.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). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,14 +28,16 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "image_etc.h" -#include "Etc.h" -#include "EtcFilter.h" +#include "image_compress_etc.h" + #include "core/io/image.h" #include "core/os/copymem.h" #include "core/os/os.h" #include "core/string/print_string.h" +#include <Etc.h> +#include <EtcFilter.h> + static Image::Format _get_etc2_mode(Image::UsedChannels format) { switch (format) { case Image::USED_CHANNELS_R: @@ -164,12 +166,12 @@ static void _compress_etc(Image *p_img, float p_lossy_quality, bool force_etc1_f int encoding_time = 0; float effort = 0.0; //default, reasonable time - if (p_lossy_quality > 0.75) { - effort = 0.4; + if (p_lossy_quality > 0.95) { + effort = 80; } else if (p_lossy_quality > 0.85) { - effort = 0.6; - } else if (p_lossy_quality > 0.95) { - effort = 0.8; + effort = 60; + } else if (p_lossy_quality > 0.75) { + effort = 40; } Etc::ErrorMetric error_metric = Etc::ErrorMetric::RGBX; // NOTE: we can experiment with other error metrics diff --git a/modules/etc/image_etc.h b/modules/etc/image_compress_etc.h index 7b4f26e127..44a06194e9 100644 --- a/modules/etc/image_etc.h +++ b/modules/etc/image_compress_etc.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* image_etc.h */ +/* image_compress_etc.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). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,9 +28,9 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef IMAGE_ETC1_H -#define IMAGE_ETC1_H +#ifndef IMAGE_COMPRESS_ETC_H +#define IMAGE_COMPRESS_ETC_H void _register_etc_compress_func(); -#endif // IMAGE_ETC_H +#endif // IMAGE_COMPRESS_ETC_H diff --git a/modules/etc/register_types.cpp b/modules/etc/register_types.cpp index 0972857808..b165bccb3e 100644 --- a/modules/etc/register_types.cpp +++ b/modules/etc/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,7 +30,7 @@ #include "register_types.h" -#include "image_etc.h" +#include "image_compress_etc.h" #include "texture_loader_pkm.h" static Ref<ResourceFormatPKM> resource_loader_pkm; diff --git a/modules/etc/register_types.h b/modules/etc/register_types.h index 247c7213af..e8cbb635ae 100644 --- a/modules/etc/register_types.h +++ b/modules/etc/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/etc/texture_loader_pkm.cpp b/modules/etc/texture_loader_pkm.cpp index c40e9612a8..b0ea109f76 100644 --- a/modules/etc/texture_loader_pkm.cpp +++ b/modules/etc/texture_loader_pkm.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/etc/texture_loader_pkm.h b/modules/etc/texture_loader_pkm.h index 6507e0bdec..67fbee3a7e 100644 --- a/modules/etc/texture_loader_pkm.h +++ b/modules/etc/texture_loader_pkm.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/fbx/README.md b/modules/fbx/README.md new file mode 100644 index 0000000000..2a2f186463 --- /dev/null +++ b/modules/fbx/README.md @@ -0,0 +1,196 @@ +# Open Source FBX Specification for the Importer + +The goal of this document is to make everything in FBX clearly stated, any errors will be corrected over time this +is a first draft. + +## fbx parser - originally from assimp + +- Folder: /modules/fbx/fbx_parser +- Upstream: assimp +- Original Version: git (308db73d0b3c2d1870cd3e465eaa283692a4cf23, 2019) +- License: BSD-3-Clause + +This can never be updated from upstream, we have heavily modified the parser to provide memory safety and add some +functionality. If anything we should give this parser back to assimp at some point as it has a lot of new features. + +# Updating assimp fbx parser + +Don't. it's not possible the code is rewritten in many areas to remove thirdparty deps and various bugs are fixed. + +Many days were put into rewriting the parser to use safe code and safe memory accessors. + +# File Headers + +FBX Binaries start with the header "Kaydara FBX Binary" + +FBX ASCII documents contain a larger header, sometimes with copyright information for a file. + +Detecting these is pretty simple. + +# What is an OP link? +It's an object to property link. It lists the properties for that object in some cases. Source and destination based by +ID. + +# What is a OO link? +Its an object to object link, it contains the ID source and destination ID. + +# FBX Node connections + +Nodes in FBX are connected using OO links, This means Object to Object. + +FBX has a single other kind of link which is Object Property, this is used for Object to Property Links, this can be + extra attributes, defaults, or even some simple settings. + +# Bones / Joints / Locators + +Bones in FBX are nodes, they initially have the Model:: Type, then have links to SubDeformer the sub deformer +is part of the skin there is also an explicit Skin link, which then links to the geometry using OO links in the +document. + +# Rotation Order in FBX compared to Godot + +**Godot uses the rotation order:** YXZ + +**FBX has dynamic rotation order to prevent gimbal lock with complex animations** + +```cpp +enum RotOrder { + RotOrder_EulerXYZ = 0 + RotOrder_EulerXZY, + RotOrder_EulerYZX, + RotOrder_EulerYXZ, + RotOrder_EulerZXY, + RotOrder_EulerZYX, + RotOrder_SphericXYZ // nobody uses this - as far as we can tell +}; +``` + + +# Pivot transforms + +### Pivot description: +- Maya and 3DS max consider everything to be in node space (bones joints, skins, lights, cameras, etc) +- Everything is a node, this means essentially nodes are auto or variants +- They are local to the node in the tree. +- They are used to calculate where a node is in space +```c++ +// For a better reference you can check editor_scene_importer_fbx.h +// references: GenFBXTransform / read the data in +// references: ComputePivotTransform / run the calculation +// This is the local pivot transform for the node, not the global transforms +Transform ComputePivotTransform( + Transform chain[TransformationComp_MAXIMUM], + Transform &geometric_transform) { + // Maya pivots + Transform T = chain[TransformationComp_Translation]; + Transform Roff = chain[TransformationComp_RotationOffset]; + Transform Rp = chain[TransformationComp_RotationPivot]; + Transform Rpre = chain[TransformationComp_PreRotation]; + Transform R = chain[TransformationComp_Rotation]; + Transform Rpost = chain[TransformationComp_PostRotation]; + Transform Soff = chain[TransformationComp_ScalingOffset]; + Transform Sp = chain[TransformationComp_ScalingPivot]; + Transform S = chain[TransformationComp_Scaling]; + + // 3DS Max Pivots + Transform OT = chain[TransformationComp_GeometricTranslation]; + Transform OR = chain[TransformationComp_GeometricRotation]; + Transform OS = chain[TransformationComp_GeometricScaling]; + + // Calculate 3DS max pivot transform - use geometric space (e.g doesn't effect children nodes only the current node) + geometric_transform = OT * OR * OS; + // Calculate standard maya pivots + return T * Roff * Rp * Rpre * R * Rpost.inverse() * Rp.inverse() * Soff * Sp * S * Sp.inverse(); +} +``` + +# Transform inheritance for FBX Nodes + +The goal of below is to explain why they implement this in the first place. + +The use case is to make nodes have an option to override their local scaling or to make scaling influenced by orientation, which i would imagine would be useful for when you need to rotate a node and the child to scale based on the orientation rather than setting on the rotation matrix planes. +```cpp +// not modified the formatting here since this code must remain clear +enum TransformInheritance { + Transform_RrSs = 0, + // Parent Rotation * Local Rotation * Parent Scale * Local Scale -- Parent Rotation Offset * Parent ScalingOffset (Local scaling is offset by rotation of parent node) + Transform_RSrs = 1, // Parent Rotation * Parent Scale * Local Rotation * Local Scale -- Parent * Local (normal mode) + Transform_Rrs = 2, // Parent Rotation * Local Rotation * Local Scale -- Node transform scale is the only relevant component + TransformInheritance_MAX // end-of-enum sentinel +}; + +enum TransformInheritance { + Transform_RrSs = 0, + // Local scaling is offset by rotation of parent node + Transform_RSrs = 1, + // Parent * Local (normal mode) + Transform_Rrs = 2, + // Node transform scale is the only relevant component + TransformInheritance_MAX // end-of-enum sentinel +}; +``` + +# Axis in FBX + +Godot has one format for the declared axis + +AXIS X, AXIS Y, -AXIS Z + +FBX supports any format you can think of. As it has to support Maya and 3DS Max. + +#### FBX File Header +```json +GlobalSettings: { + Version: 1000 + Properties70: { + P: "UpAxis", "int", "Integer", "",1 + P: "UpAxisSign", "int", "Integer", "",1 + P: "FrontAxis", "int", "Integer", "",2 + P: "FrontAxisSign", "int", "Integer", "",1 + P: "CoordAxis", "int", "Integer", "",0 + P: "CoordAxisSign", "int", "Integer", "",1 + P: "OriginalUpAxis", "int", "Integer", "",1 + P: "OriginalUpAxisSign", "int", "Integer", "",1 + P: "UnitScaleFactor", "double", "Number", "",1 + P: "OriginalUnitScaleFactor", "double", "Number", "",1 + P: "AmbientColor", "ColorRGB", "Color", "",0,0,0 + P: "DefaultCamera", "KString", "", "", "Producer Perspective" + P: "TimeMode", "enum", "", "",6 + P: "TimeProtocol", "enum", "", "",2 + P: "SnapOnFrameMode", "enum", "", "",0 + P: "TimeSpanStart", "KTime", "Time", "",0 + P: "TimeSpanStop", "KTime", "Time", "",92372316000 + P: "CustomFrameRate", "double", "Number", "",-1 + P: "TimeMarker", "Compound", "", "" + P: "CurrentTimeMarker", "int", "Integer", "",-1 + } +} +``` + +#### FBX FILE declares axis dynamically using FBX header +Coord is X +Up is Y +Front is Z + +#### GODOT - constant reference point +Coord is X positive, +Y is up positive, +Front is -Z negative + +### Explaining MeshGeometry indexing + +Reference type declared: +- Direct (directly related to the mapping information type) +- IndexToDirect (Map with key value, meaning depends on the MappingInformationType) + +ControlPoint is a vertex +* None The mapping is undetermined. +* ByVertex There will be one mapping coordinate for each surface control point/vertex. + * If you have direct reference type vertices [x] + * If you have IndexToDirect reference type the UV +* ByPolygonVertex There will be one mapping coordinate for each vertex, for every polygon of which it is a part. This means that a vertex will have as many mapping coordinates as polygons of which it is a part. (Sorted by polygon, referencing vertex) +* ByPolygon There can be only one mapping coordinate for the whole polygon. + * One mapping per polygon polygon x has this normal x + * For each vertex of the polygon then set the normal to x +* ByEdge There will be one mapping coordinate for each unique edge in the mesh. This is meant to be used with smoothing layer elements. (Mapping is referencing the edge id) +* AllSame There can be only one mapping coordinate for the whole surface. diff --git a/modules/fbx/SCsub b/modules/fbx/SCsub new file mode 100644 index 0000000000..84220a66fa --- /dev/null +++ b/modules/fbx/SCsub @@ -0,0 +1,15 @@ +#!/usr/bin/env python + +Import("env") +Import("env_modules") + +env_fbx = env_modules.Clone() + +# Make includes relative to the folder path specified here so our includes are clean +env_fbx.Prepend(CPPPATH=["#modules/fbx/"]) + +# Godot's own source files +env_fbx.add_source_files(env.modules_sources, "tools/*.cpp") +env_fbx.add_source_files(env.modules_sources, "data/*.cpp") +env_fbx.add_source_files(env.modules_sources, "fbx_parser/*.cpp") +env_fbx.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/fbx/config.py b/modules/fbx/config.py new file mode 100644 index 0000000000..78929800b3 --- /dev/null +++ b/modules/fbx/config.py @@ -0,0 +1,16 @@ +def can_build(env, platform): + return env["tools"] + + +def configure(env): + pass + + +def get_doc_classes(): + return [ + "EditorSceneImporterFBX", + ] + + +def get_doc_path(): + return "doc_classes" diff --git a/modules/fbx/data/fbx_anim_container.h b/modules/fbx/data/fbx_anim_container.h new file mode 100644 index 0000000000..8c25d65871 --- /dev/null +++ b/modules/fbx/data/fbx_anim_container.h @@ -0,0 +1,46 @@ +/*************************************************************************/ +/* fbx_anim_container.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef FBX_ANIM_CONTAINER_H +#define FBX_ANIM_CONTAINER_H + +#include "core/math/vector3.h" + +// Generic keyframes 99.99 percent of files will be vector3, except if quat interp is used, or visibility tracks +// FBXTrack is used in a map in the implementation in fbx/editor_scene_importer_fbx.cpp +// to avoid having to rewrite the entire logic I refactored this into the code instead. +// once it works I can rewrite so we can add the fun misc features / small features +struct FBXTrack { + bool has_default = false; + Vector3 default_value; + std::map<int64_t, Vector3> keyframes; +}; + +#endif //MODEL_ABSTRACTION_ANIM_CONTAINER_H diff --git a/modules/fbx/data/fbx_bone.cpp b/modules/fbx/data/fbx_bone.cpp new file mode 100644 index 0000000000..38dada33af --- /dev/null +++ b/modules/fbx/data/fbx_bone.cpp @@ -0,0 +1,56 @@ +/*************************************************************************/ +/* fbx_bone.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "fbx_bone.h" + +#include "fbx_node.h" +#include "import_state.h" + +Ref<FBXNode> FBXSkinDeformer::get_link(const ImportState &state) const { + print_verbose("bone name: " + bone->bone_name); + + // safe for when deformers must be polyfilled when skin has different count of binds to bones in the scene ;) + if (!cluster) { + return nullptr; + } + + ERR_FAIL_COND_V_MSG(cluster->TargetNode() == nullptr, nullptr, "bone has invalid target node"); + + Ref<FBXNode> link_node; + uint64_t id = cluster->TargetNode()->ID(); + if (state.fbx_target_map.has(id)) { + link_node = state.fbx_target_map[id]; + } else { + print_error("link node not found for " + itos(id)); + } + + // the node in space this is for, like if it's FOR a target. + return link_node; +} diff --git a/modules/fbx/data/fbx_bone.h b/modules/fbx/data/fbx_bone.h new file mode 100644 index 0000000000..efba147b89 --- /dev/null +++ b/modules/fbx/data/fbx_bone.h @@ -0,0 +1,90 @@ +/*************************************************************************/ +/* fbx_bone.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef FBX_BONE_H +#define FBX_BONE_H + +#include "fbx_node.h" +#include "import_state.h" + +#include "fbx_parser/FBXDocument.h" + +struct PivotTransform; + +struct FBXBone : public 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 diff --git a/modules/fbx/data/fbx_material.cpp b/modules/fbx/data/fbx_material.cpp new file mode 100644 index 0000000000..5995097b2f --- /dev/null +++ b/modules/fbx/data/fbx_material.cpp @@ -0,0 +1,464 @@ +/*************************************************************************/ +/* fbx_material.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "fbx_material.h" +#include "scene/resources/material.h" +#include "scene/resources/texture.h" +#include "tools/validation_tools.h" + +String FBXMaterial::get_material_name() const { + return material_name; +} + +void FBXMaterial::set_imported_material(FBXDocParser::Material *p_material) { + material = p_material; +} + +void FBXMaterial::add_search_string(String p_filename, String p_current_directory, String search_directory, Vector<String> &texture_search_paths) { + if (search_directory.is_empty()) { + texture_search_paths.push_back(p_current_directory.get_base_dir().plus_file(p_filename)); + } else { + texture_search_paths.push_back(p_current_directory.get_base_dir().plus_file(search_directory + "/" + p_filename)); + texture_search_paths.push_back(p_current_directory.get_base_dir().plus_file("../" + search_directory + "/" + p_filename)); + } +} + +String find_file(const String &p_base, const String &p_file_to_find) { + _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.is_empty(), "your texture has no file extension so we had to ignore it, let us know if you think this is wrong file an issue on github! " + debug_string); + ERR_CONTINUE_MSG(fbx_texture_map.count(fbx_mapping_name) <= 0, "This material has a texture with mapping name: " + String(fbx_mapping_name.c_str()) + " which is not yet supported by this importer. Consider opening an issue so we can support it."); + ERR_CONTINUE_MSG( + file_extension_uppercase != "PNG" && + file_extension_uppercase != "JPEG" && + file_extension_uppercase != "JPG" && + file_extension_uppercase != "TGA" && + file_extension_uppercase != "WEBP" && + file_extension_uppercase != "DDS", + "The FBX file contains a texture with an unrecognized extension: " + file_extension_uppercase); + + print_verbose("Getting FBX mapping mode for " + String(fbx_mapping_name.c_str())); + // get the texture map type + const StandardMaterial3D::TextureParam mapping_mode = fbx_texture_map.at(fbx_mapping_name); + print_verbose("Set FBX mapping mode to " + get_texture_param_name(mapping_mode)); + + Ref<Texture> texture; + print_verbose("texture mapping name: " + texture_name); + + if (state.cached_image_searches.has(texture_name)) { + texture = state.cached_image_searches[texture_name]; + } else { + String path = find_texture_path_by_filename(texture_name, p_fbx_current_directory); + if (!path.is_empty()) { + Ref<Texture2D> image_texture = ResourceLoader::load(path); + + ERR_CONTINUE(image_texture.is_null()); + + texture = image_texture; + state.cached_image_searches.insert(texture_name, texture); + print_verbose("Created texture from loaded image file."); + + } else if (fbx_texture_data != nullptr && fbx_texture_data->Media() != nullptr && fbx_texture_data->Media()->IsEmbedded()) { + // This is an embedded texture. Extract it. + Ref<Image> image; + //image.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; +} diff --git a/modules/fbx/data/fbx_material.h b/modules/fbx/data/fbx_material.h new file mode 100644 index 0000000000..e974a256f6 --- /dev/null +++ b/modules/fbx/data/fbx_material.h @@ -0,0 +1,286 @@ +/*************************************************************************/ +/* fbx_material.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef FBX_MATERIAL_H +#define FBX_MATERIAL_H + +#include "tools/import_utils.h" + +#include "core/object/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 diff --git a/modules/fbx/data/fbx_mesh_data.cpp b/modules/fbx/data/fbx_mesh_data.cpp new file mode 100644 index 0000000000..963a815896 --- /dev/null +++ b/modules/fbx/data/fbx_mesh_data.cpp @@ -0,0 +1,1447 @@ +/*************************************************************************/ +/* fbx_mesh_data.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "fbx_mesh_data.h" + +#include "core/templates/local_vector.h" +#include "scene/resources/mesh.h" +#include "scene/resources/surface_tool.h" + +#include "thirdparty/misc/triangulator.h" + +template <class T> +T collect_first(const Vector<VertexData<T>> *p_data, T p_fall_back) { + if (p_data->is_empty()) { + return p_fall_back; + } + + return (*p_data)[0].data; +} + +template <class T> +HashMap<int, T> collect_all(const Vector<VertexData<T>> *p_data, HashMap<int, T> p_fall_back) { + if (p_data->is_empty()) { + return p_fall_back; + } + + HashMap<int, T> collection; + for (int i = 0; i < p_data->size(); i += 1) { + const VertexData<T> &vd = (*p_data)[i]; + collection[vd.polygon_index] = vd.data; + } + return collection; +} + +template <class T> +T collect_average(const Vector<VertexData<T>> *p_data, T p_fall_back) { + if (p_data->is_empty()) { + return p_fall_back; + } + + T combined = (*p_data)[0].data; // Make sure the data is always correctly initialized. + print_verbose("size of data: " + itos(p_data->size())); + for (int i = 1; i < p_data->size(); i += 1) { + combined += (*p_data)[i].data; + } + combined = combined / real_t(p_data->size()); + + return combined.normalized(); +} + +HashMap<int, Vector3> collect_normal(const Vector<VertexData<Vector3>> *p_data, HashMap<int, Vector3> p_fall_back) { + if (p_data->is_empty()) { + return p_fall_back; + } + + HashMap<int, Vector3> collection; + for (int i = 0; i < p_data->size(); i += 1) { + const VertexData<Vector3> &vd = (*p_data)[i]; + collection[vd.polygon_index] = vd.data; + } + return collection; +} + +HashMap<int, Vector2> collect_uv(const Vector<VertexData<Vector2>> *p_data, HashMap<int, Vector2> p_fall_back) { + if (p_data->is_empty()) { + return p_fall_back; + } + + HashMap<int, Vector2> collection; + for (int i = 0; i < p_data->size(); i += 1) { + const VertexData<Vector2> &vd = (*p_data)[i]; + collection[vd.polygon_index] = vd.data; + } + return collection; +} + +typedef int Vertex; +typedef int SurfaceId; +typedef int PolygonId; +typedef int DataIndex; + +struct SurfaceData { + Ref<SurfaceTool> surface_tool; + OrderedHashMap<Vertex, int> lookup_table; // proposed fix is to replace lookup_table[vertex_id] to give the position of the vertices_map[int] index. + LocalVector<Vertex> vertices_map; // this must be ordered the same as insertion <-- slow to do find() operation. + Ref<Material> material; + HashMap<PolygonId, Vector<DataIndex>> surface_polygon_vertex; + Array morphs; +}; + +EditorSceneImporterMeshNode3D *FBXMeshData::create_fbx_mesh(const ImportState &state, const FBXDocParser::MeshGeometry *p_mesh_geometry, const FBXDocParser::Model *model, bool use_compression) { + mesh_geometry = p_mesh_geometry; + // todo: make this just use a uint64_t FBX ID this is a copy of our original materials unfortunately. + const std::vector<const FBXDocParser::Material *> &material_lookup = model->GetMaterials(); + + // TODO: perf hotspot on large files + // this can be a very large copy + std::vector<int> polygon_indices = mesh_geometry->get_polygon_indices(); + std::vector<Vector3> vertices = mesh_geometry->get_vertices(); + + // Phase 1. Parse all FBX data. + HashMap<int, Vector3> normals; + HashMap<int, HashMap<int, Vector3>> normals_raw = extract_per_vertex_data( + vertices.size(), + mesh_geometry->get_edge_map(), + polygon_indices, + mesh_geometry->get_normals(), + &collect_all, + HashMap<int, Vector3>()); + + HashMap<int, Vector2> uvs_0; + HashMap<int, HashMap<int, Vector2>> uvs_0_raw = extract_per_vertex_data( + vertices.size(), + mesh_geometry->get_edge_map(), + polygon_indices, + mesh_geometry->get_uv_0(), + &collect_all, + HashMap<int, Vector2>()); + + HashMap<int, Vector2> uvs_1; + HashMap<int, HashMap<int, Vector2>> uvs_1_raw = extract_per_vertex_data( + vertices.size(), + mesh_geometry->get_edge_map(), + polygon_indices, + mesh_geometry->get_uv_1(), + &collect_all, + HashMap<int, Vector2>()); + + HashMap<int, Color> colors; + HashMap<int, HashMap<int, Color>> colors_raw = extract_per_vertex_data( + vertices.size(), + mesh_geometry->get_edge_map(), + polygon_indices, + mesh_geometry->get_colors(), + &collect_all, + HashMap<int, Color>()); + + // TODO what about tangents? + // TODO what about bi-nomials? + // TODO there is other? + + HashMap<int, SurfaceId> polygon_surfaces = extract_per_polygon( + vertices.size(), + polygon_indices, + mesh_geometry->get_material_allocation_id(), + -1); + + HashMap<String, MorphVertexData> morphs; + extract_morphs(mesh_geometry, morphs); + + // TODO please add skinning. + //mesh_id = mesh_geometry->ID(); + + sanitize_vertex_weights(state); + + // Re organize polygon vertices to to correctly take into account strange + // UVs. + reorganize_vertices( + polygon_indices, + vertices, + normals, + uvs_0, + uvs_1, + colors, + morphs, + normals_raw, + colors_raw, + uvs_0_raw, + uvs_1_raw); + + const int color_count = colors.size(); + print_verbose("Vertex color count: " + itos(color_count)); + + // Make sure that from this moment on the mesh_geometry is no used anymore. + // This is a safety step, because the mesh_geometry data are no more valid + // at this point. + + const int vertex_count = vertices.size(); + + print_verbose("Vertex count: " + itos(vertex_count)); + + // The map key is the material allocator id that is also used as surface id. + HashMap<SurfaceId, SurfaceData> surfaces; + + // Phase 2. For each material create a surface tool (So a different mesh). + { + if (polygon_surfaces.is_empty()) { + // No material, just use the default one with index -1. + // Set -1 to all polygons. + const int polygon_count = count_polygons(polygon_indices); + for (int p = 0; p < polygon_count; p += 1) { + polygon_surfaces[p] = -1; + } + } + + // Create the surface now. + for (const int *polygon_id = polygon_surfaces.next(nullptr); polygon_id != nullptr; polygon_id = polygon_surfaces.next(polygon_id)) { + const int surface_id = polygon_surfaces[*polygon_id]; + if (surfaces.has(surface_id) == false) { + SurfaceData sd; + sd.surface_tool.instance(); + sd.surface_tool->begin(Mesh::PRIMITIVE_TRIANGLES); + + if (surface_id < 0) { + // nothing to do + } else if (surface_id < (int)material_lookup.size()) { + const FBXDocParser::Material *mat_mapping = material_lookup.at(surface_id); + const uint64_t mapping_id = mat_mapping->ID(); + if (state.cached_materials.has(mapping_id)) { + sd.material = state.cached_materials[mapping_id]; + } + } else { + WARN_PRINT("out of bounds surface detected, FBX file has corrupt material data"); + } + + surfaces.set(surface_id, sd); + } + } + } + + // Phase 3. Map the vertices relative to each surface, in this way we can + // just insert the vertices that we need per each surface. + { + PolygonId polygon_index = -1; + SurfaceId surface_id = -1; + SurfaceData *surface_data = nullptr; + + for (size_t polygon_vertex = 0; polygon_vertex < polygon_indices.size(); polygon_vertex += 1) { + if (is_start_of_polygon(polygon_indices, polygon_vertex)) { + polygon_index += 1; + ERR_FAIL_COND_V_MSG(polygon_surfaces.has(polygon_index) == false, nullptr, "The FBX file is corrupted, This surface_index is not expected."); + surface_id = polygon_surfaces[polygon_index]; + surface_data = surfaces.getptr(surface_id); + CRASH_COND(surface_data == nullptr); // Can't be null. + } + + const int vertex = get_vertex_from_polygon_vertex(polygon_indices, polygon_vertex); + + // The vertex position in the surface + // Uses a lookup table for speed with large scenes + bool has_polygon_vertex_index = surface_data->lookup_table.has(vertex); + int surface_polygon_vertex_index = -1; + + if (has_polygon_vertex_index) { + surface_polygon_vertex_index = surface_data->lookup_table[vertex]; + } else { + surface_polygon_vertex_index = surface_data->vertices_map.size(); + surface_data->lookup_table[vertex] = surface_polygon_vertex_index; + surface_data->vertices_map.push_back(vertex); + } + + surface_data->surface_polygon_vertex[polygon_index].push_back(surface_polygon_vertex_index); + } + } + + //print_verbose("[debug UV 1] UV1: " + itos(uvs_0.size())); + //print_verbose("[debug UV 2] UV2: " + itos(uvs_1.size())); + + // Phase 4. Per each surface just insert the vertices and add the indices. + for (const SurfaceId *surface_id = surfaces.next(nullptr); surface_id != nullptr; surface_id = surfaces.next(surface_id)) { + SurfaceData *surface = surfaces.getptr(*surface_id); + + // Just add the vertices data. + for (unsigned int i = 0; i < surface->vertices_map.size(); i += 1) { + const Vertex vertex = surface->vertices_map[i]; + + // This must be done before add_vertex because the surface tool is + // expecting this before the st->add_vertex() call + add_vertex(state, + surface->surface_tool, + state.scale, + vertex, + vertices, + normals, + uvs_0, + uvs_1, + colors); + } + + // Triangulate the various polygons and add the indices. + for (const PolygonId *polygon_id = surface->surface_polygon_vertex.next(nullptr); polygon_id != nullptr; polygon_id = surface->surface_polygon_vertex.next(polygon_id)) { + const Vector<DataIndex> *indices = surface->surface_polygon_vertex.getptr(*polygon_id); + + triangulate_polygon( + surface->surface_tool, + *indices, + surface->vertices_map, + vertices); + } + } + + // Phase 5. Compose the morphs if any. + for (const SurfaceId *surface_id = surfaces.next(nullptr); surface_id != nullptr; surface_id = surfaces.next(surface_id)) { + SurfaceData *surface = surfaces.getptr(*surface_id); + + for (const String *morph_name = morphs.next(nullptr); morph_name != nullptr; morph_name = morphs.next(morph_name)) { + MorphVertexData *morph_data = morphs.getptr(*morph_name); + + // As said by the docs, this is not supposed to be different than + // vertex_count. + CRASH_COND(morph_data->vertices.size() != vertex_count); + CRASH_COND(morph_data->normals.size() != vertex_count); + + Vector3 *vertices_ptr = morph_data->vertices.ptrw(); + Vector3 *normals_ptr = morph_data->normals.ptrw(); + + Ref<SurfaceTool> morph_st; + morph_st.instance(); + morph_st->begin(Mesh::PRIMITIVE_TRIANGLES); + + for (unsigned int vi = 0; vi < surface->vertices_map.size(); vi += 1) { + const Vertex vertex = surface->vertices_map[vi]; + add_vertex( + state, + morph_st, + state.scale, + vertex, + vertices, + normals, + uvs_0, + uvs_1, + colors, + vertices_ptr[vertex], + normals_ptr[vertex]); + } + + if (state.is_blender_fbx) { + morph_st->generate_normals(); + } + morph_st->generate_tangents(); + surface->morphs.push_back(morph_st->commit_to_arrays()); + } + } + + // Phase 6. Compose the mesh and return it. + Ref<EditorSceneImporterMesh> mesh; + mesh.instance(); + + // Add blend shape info. + for (const String *morph_name = morphs.next(nullptr); morph_name != nullptr; morph_name = morphs.next(morph_name)) { + mesh->add_blend_shape(*morph_name); + } + + // TODO always normalized, Why? + mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_NORMALIZED); + + // Add surfaces. + int in_mesh_surface_id = 0; + for (const SurfaceId *surface_id = surfaces.next(nullptr); surface_id != nullptr; surface_id = surfaces.next(surface_id)) { + SurfaceData *surface = surfaces.getptr(*surface_id); + + if (state.is_blender_fbx) { + surface->surface_tool->generate_normals(); + } + // you can't generate them without a valid uv map. + if (uvs_0_raw.size() > 0) { + surface->surface_tool->generate_tangents(); + } + + Array mesh_array = surface->surface_tool->commit_to_arrays(); + Array blend_shapes = surface->morphs; + + if (surface->material.is_valid()) { + mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES, mesh_array, blend_shapes, Dictionary(), surface->material, surface->material->get_name()); + } else { + mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES, mesh_array, blend_shapes); + } + + in_mesh_surface_id += 1; + } + + EditorSceneImporterMeshNode3D *godot_mesh = memnew(EditorSceneImporterMeshNode3D); + godot_mesh->set_mesh(mesh); + return godot_mesh; +} + +void FBXMeshData::sanitize_vertex_weights(const ImportState &state) { + const int max_vertex_influence_count = RS::ARRAY_WEIGHTS_SIZE; + Map<int, int> skeleton_to_skin_bind_id; + // TODO: error's need added + const FBXDocParser::Skin *fbx_skin = mesh_geometry->DeformerSkin(); + + if (fbx_skin == nullptr || fbx_skin->Clusters().size() == 0) { + return; // do nothing + } + + // + // Precalculate the skin cluster mapping + // + + int bind_id = 0; + for (const FBXDocParser::Cluster *cluster : fbx_skin->Clusters()) { + Ref<FBXBone> bone = state.fbx_bone_map[cluster->TargetNode()->ID()]; + skeleton_to_skin_bind_id.insert(bone->godot_bone_id, bind_id); + bind_id++; + } + + for (const Vertex *v = vertex_weights.next(nullptr); v != nullptr; v = vertex_weights.next(v)) { + VertexWeightMapping *vm = vertex_weights.getptr(*v); + ERR_CONTINUE(vm->bones.size() != vm->weights.size()); // No message, already checked. + ERR_CONTINUE(vm->bones_ref.size() != vm->weights.size()); // No message, already checked. + + const int initial_size = vm->weights.size(); + { + // Init bone id + int *bones_ptr = vm->bones.ptrw(); + Ref<FBXBone> *bones_ref_ptr = vm->bones_ref.ptrw(); + + for (int i = 0; i < vm->weights.size(); i += 1) { + // At this point this is not possible because the skeleton is already initialized. + CRASH_COND(bones_ref_ptr[i]->godot_bone_id == -2); + bones_ptr[i] = skeleton_to_skin_bind_id[bones_ref_ptr[i]->godot_bone_id]; + } + + // From this point on the data is no more valid. + vm->bones_ref.clear(); + } + + { + // Sort + real_t *weights_ptr = vm->weights.ptrw(); + int *bones_ptr = vm->bones.ptrw(); + for (int i = 0; i < vm->weights.size(); i += 1) { + for (int x = i + 1; x < vm->weights.size(); x += 1) { + if (weights_ptr[i] < weights_ptr[x]) { + SWAP(weights_ptr[i], weights_ptr[x]); + SWAP(bones_ptr[i], bones_ptr[x]); + } + } + } + } + + { + // Resize + vm->weights.resize(max_vertex_influence_count); + vm->bones.resize(max_vertex_influence_count); + real_t *weights_ptr = vm->weights.ptrw(); + int *bones_ptr = vm->bones.ptrw(); + for (int i = initial_size; i < max_vertex_influence_count; i += 1) { + weights_ptr[i] = 0.0; + bones_ptr[i] = 0; + } + + // Normalize + real_t sum = 0.0; + for (int i = 0; i < max_vertex_influence_count; i += 1) { + sum += weights_ptr[i]; + } + if (sum > 0.0) { + for (int i = 0; i < vm->weights.size(); i += 1) { + weights_ptr[i] = weights_ptr[i] / sum; + } + } + } + } +} + +void FBXMeshData::reorganize_vertices( + // TODO: perf hotspot on insane files + std::vector<int> &r_polygon_indices, + std::vector<Vector3> &r_vertices, + HashMap<int, Vector3> &r_normals, + HashMap<int, Vector2> &r_uv_1, + HashMap<int, Vector2> &r_uv_2, + HashMap<int, Color> &r_color, + HashMap<String, MorphVertexData> &r_morphs, + HashMap<int, HashMap<int, Vector3>> &r_normals_raw, + HashMap<int, HashMap<int, Color>> &r_colors_raw, + HashMap<int, HashMap<int, Vector2>> &r_uv_1_raw, + HashMap<int, HashMap<int, Vector2>> &r_uv_2_raw) { + // Key: OldVertex; Value: [New vertices]; + HashMap<int, Vector<int>> duplicated_vertices; + + PolygonId polygon_index = -1; + for (int pv = 0; pv < (int)r_polygon_indices.size(); pv += 1) { + if (is_start_of_polygon(r_polygon_indices, pv)) { + polygon_index += 1; + } + const Vertex index = get_vertex_from_polygon_vertex(r_polygon_indices, pv); + + bool need_duplication = false; + Vector2 this_vert_poly_uv1 = Vector2(); + Vector2 this_vert_poly_uv2 = Vector2(); + Vector3 this_vert_poly_normal = Vector3(); + Color this_vert_poly_color = Color(); + + // Take the normal and see if we need to duplicate this polygon. + if (r_normals_raw.has(index)) { + const HashMap<PolygonId, Vector3> *nrml_arr = r_normals_raw.getptr(index); + + if (nrml_arr->has(polygon_index)) { + this_vert_poly_normal = nrml_arr->get(polygon_index); + } else if (nrml_arr->has(-1)) { + this_vert_poly_normal = nrml_arr->get(-1); + } else { + print_error("invalid normal detected: " + itos(index) + " polygon index: " + itos(polygon_index)); + for (const PolygonId *pid = nrml_arr->next(nullptr); pid != nullptr; pid = nrml_arr->next(pid)) { + print_verbose("debug contents key: " + itos(*pid)); + + if (nrml_arr->has(*pid)) { + print_verbose("contents valid: " + nrml_arr->get(*pid)); + } + } + } + + // Now, check if we need to duplicate it. + for (const PolygonId *pid = nrml_arr->next(nullptr); pid != nullptr; pid = nrml_arr->next(pid)) { + if (*pid == polygon_index) { + continue; + } + + const Vector3 vert_poly_normal = *nrml_arr->getptr(*pid); + if ((this_vert_poly_normal - vert_poly_normal).length_squared() > CMP_EPSILON) { + // Yes this polygon need duplication. + need_duplication = true; + break; + } + } + } + + // TODO: make me vertex color + // Take the normal and see if we need to duplicate this polygon. + if (r_colors_raw.has(index)) { + const HashMap<PolygonId, Color> *color_arr = r_colors_raw.getptr(index); + + if (color_arr->has(polygon_index)) { + this_vert_poly_color = color_arr->get(polygon_index); + } else if (color_arr->has(-1)) { + this_vert_poly_color = color_arr->get(-1); + } else { + print_error("invalid color detected: " + itos(index) + " polygon index: " + itos(polygon_index)); + for (const PolygonId *pid = color_arr->next(nullptr); pid != nullptr; pid = color_arr->next(pid)) { + print_verbose("debug contents key: " + itos(*pid)); + + if (color_arr->has(*pid)) { + print_verbose("contents valid: " + color_arr->get(*pid)); + } + } + } + + // Now, check if we need to duplicate it. + for (const PolygonId *pid = color_arr->next(nullptr); pid != nullptr; pid = color_arr->next(pid)) { + if (*pid == polygon_index) { + continue; + } + + const Color vert_poly_color = *color_arr->getptr(*pid); + if (!this_vert_poly_color.is_equal_approx(vert_poly_color)) { + // Yes this polygon need duplication. + need_duplication = true; + break; + } + } + } + + // Take the UV1 and UV2 and see if we need to duplicate this polygon. + { + HashMap<int, HashMap<int, Vector2>> *uv_raw = &r_uv_1_raw; + Vector2 *this_vert_poly_uv = &this_vert_poly_uv1; + for (int kk = 0; kk < 2; kk++) { + if (uv_raw->has(index)) { + const HashMap<PolygonId, Vector2> *uvs = uv_raw->getptr(index); + + if (uvs->has(polygon_index)) { + // This Polygon has its own uv. + (*this_vert_poly_uv) = *uvs->getptr(polygon_index); + + // Check if we need to duplicate it. + for (const PolygonId *pid = uvs->next(nullptr); pid != nullptr; pid = uvs->next(pid)) { + if (*pid == polygon_index) { + continue; + } + const Vector2 vert_poly_uv = *uvs->getptr(*pid); + if (((*this_vert_poly_uv) - vert_poly_uv).length_squared() > CMP_EPSILON) { + // Yes this polygon need duplication. + need_duplication = true; + break; + } + } + } else if (uvs->has(-1)) { + // It has the default UV. + (*this_vert_poly_uv) = *uvs->getptr(-1); + } else if (uvs->size() > 0) { + // No uv, this is strange, just take the first and duplicate. + (*this_vert_poly_uv) = *uvs->getptr(*uvs->next(nullptr)); + WARN_PRINT("No UVs for this polygon, while there is no default and some other polygons have it. This FBX file may be corrupted."); + } + } + uv_raw = &r_uv_2_raw; + this_vert_poly_uv = &this_vert_poly_uv2; + } + } + + // If we want to duplicate it, Let's see if we already duplicated this + // vertex. + if (need_duplication) { + if (duplicated_vertices.has(index)) { + Vertex similar_vertex = -1; + // Let's see if one of the new vertices has the same data of this. + const Vector<int> *new_vertices = duplicated_vertices.getptr(index); + for (int j = 0; j < new_vertices->size(); j += 1) { + const Vertex new_vertex = (*new_vertices)[j]; + bool same_uv1 = false; + bool same_uv2 = false; + bool same_normal = false; + bool same_color = false; + + if (r_uv_1.has(new_vertex)) { + if ((this_vert_poly_uv1 - (*r_uv_1.getptr(new_vertex))).length_squared() <= CMP_EPSILON) { + same_uv1 = true; + } + } + + if (r_uv_2.has(new_vertex)) { + if ((this_vert_poly_uv2 - (*r_uv_2.getptr(new_vertex))).length_squared() <= CMP_EPSILON) { + same_uv2 = true; + } + } + + if (r_color.has(new_vertex)) { + if (this_vert_poly_color.is_equal_approx((*r_color.getptr(new_vertex)))) { + same_color = true; + } + } + + if (r_normals.has(new_vertex)) { + if ((this_vert_poly_normal - (*r_normals.getptr(new_vertex))).length_squared() <= CMP_EPSILON) { + same_uv2 = true; + } + } + + if (same_uv1 && same_uv2 && same_normal && same_color) { + similar_vertex = new_vertex; + break; + } + } + + if (similar_vertex != -1) { + // Update polygon. + if (is_end_of_polygon(r_polygon_indices, pv)) { + r_polygon_indices[pv] = ~similar_vertex; + } else { + r_polygon_indices[pv] = similar_vertex; + } + need_duplication = false; + } + } + } + + if (need_duplication) { + const Vertex old_index = index; + const Vertex new_index = r_vertices.size(); + + // Polygon index. + if (is_end_of_polygon(r_polygon_indices, pv)) { + r_polygon_indices[pv] = ~new_index; + } else { + r_polygon_indices[pv] = new_index; + } + + // Vertex position. + r_vertices.push_back(r_vertices[old_index]); + + // Normals + if (r_normals_raw.has(old_index)) { + r_normals.set(new_index, this_vert_poly_normal); + r_normals_raw.getptr(old_index)->erase(polygon_index); + r_normals_raw[new_index][polygon_index] = this_vert_poly_normal; + } + + // Vertex Color + if (r_colors_raw.has(old_index)) { + r_color.set(new_index, this_vert_poly_color); + r_colors_raw.getptr(old_index)->erase(polygon_index); + r_colors_raw[new_index][polygon_index] = this_vert_poly_color; + } + + // UV 0 + if (r_uv_1_raw.has(old_index)) { + r_uv_1.set(new_index, this_vert_poly_uv1); + r_uv_1_raw.getptr(old_index)->erase(polygon_index); + r_uv_1_raw[new_index][polygon_index] = this_vert_poly_uv1; + } + + // UV 1 + if (r_uv_2_raw.has(old_index)) { + r_uv_2.set(new_index, this_vert_poly_uv2); + r_uv_2_raw.getptr(old_index)->erase(polygon_index); + r_uv_2_raw[new_index][polygon_index] = this_vert_poly_uv2; + } + + // Morphs + for (const String *mname = r_morphs.next(nullptr); mname != nullptr; mname = r_morphs.next(mname)) { + MorphVertexData *d = r_morphs.getptr(*mname); + // This can't never happen. + CRASH_COND(d == nullptr); + if (d->vertices.size() > old_index) { + d->vertices.push_back(d->vertices[old_index]); + } + if (d->normals.size() > old_index) { + d->normals.push_back(d->normals[old_index]); + } + } + + if (vertex_weights.has(old_index)) { + vertex_weights.set(new_index, vertex_weights[old_index]); + } + + duplicated_vertices[old_index].push_back(new_index); + } else { + if (r_normals_raw.has(index) && + r_normals.has(index) == false) { + r_normals.set(index, this_vert_poly_normal); + } + + if (r_colors_raw.has(index) && r_color.has(index) == false) { + r_color.set(index, this_vert_poly_color); + } + + if (r_uv_1_raw.has(index) && + r_uv_1.has(index) == false) { + r_uv_1.set(index, this_vert_poly_uv1); + } + + if (r_uv_2_raw.has(index) && + r_uv_2.has(index) == false) { + r_uv_2.set(index, this_vert_poly_uv2); + } + } + } +} + +void FBXMeshData::add_vertex( + const ImportState &state, + Ref<SurfaceTool> p_surface_tool, + real_t p_scale, + Vertex p_vertex, + const std::vector<Vector3> &p_vertices_position, + const HashMap<int, Vector3> &p_normals, + const HashMap<int, Vector2> &p_uvs_0, + const HashMap<int, Vector2> &p_uvs_1, + const HashMap<int, Color> &p_colors, + const Vector3 &p_morph_value, + const Vector3 &p_morph_normal) { + ERR_FAIL_INDEX_MSG(p_vertex, (Vertex)p_vertices_position.size(), "FBX file is corrupted, the position of the vertex can't be retrieved."); + + if (p_normals.has(p_vertex) && !state.is_blender_fbx) { + p_surface_tool->set_normal(p_normals[p_vertex] + p_morph_normal); + } + + if (p_uvs_0.has(p_vertex)) { + //print_verbose("uv1: [" + itos(p_vertex) + "] " + p_uvs_0[p_vertex]); + // Inverts Y UV. + p_surface_tool->set_uv(Vector2(p_uvs_0[p_vertex].x, 1 - p_uvs_0[p_vertex].y)); + } + + if (p_uvs_1.has(p_vertex)) { + //print_verbose("uv2: [" + itos(p_vertex) + "] " + p_uvs_1[p_vertex]); + // Inverts Y UV. + p_surface_tool->set_uv2(Vector2(p_uvs_1[p_vertex].x, 1 - p_uvs_1[p_vertex].y)); + } + + if (p_colors.has(p_vertex)) { + p_surface_tool->set_color(p_colors[p_vertex]); + } + + // TODO what about binormals? + // TODO there is other? + + if (vertex_weights.has(p_vertex)) { + // Let's extract the weight info. + const VertexWeightMapping *vm = vertex_weights.getptr(p_vertex); + const Vector<int> &bones = vm->bones; + + // the bug is that the bone idx is wrong because it is not ref'ing the skin. + + if (bones.size() > RS::ARRAY_WEIGHTS_SIZE) { + print_error("[weight overflow detected]"); + } + + p_surface_tool->set_weights(vm->weights); + // 0 1 2 3 4 5 6 7 < local skeleton / skin for mesh + // 0 1 2 3 4 5 6 7 8 9 10 < actual skeleton with all joints + p_surface_tool->set_bones(bones); + } + + // The surface tool want the vertex position as last thing. + p_surface_tool->add_vertex((p_vertices_position[p_vertex] + p_morph_value) * p_scale); +} + +void FBXMeshData::triangulate_polygon(Ref<SurfaceTool> st, Vector<int> p_polygon_vertex, const Vector<Vertex> p_surface_vertex_map, const std::vector<Vector3> &p_vertices) const { + const int polygon_vertex_count = p_polygon_vertex.size(); + if (polygon_vertex_count == 1) { + // point to triangle + st->add_index(p_polygon_vertex[0]); + st->add_index(p_polygon_vertex[0]); + st->add_index(p_polygon_vertex[0]); + return; + } else if (polygon_vertex_count == 2) { + // line to triangle + st->add_index(p_polygon_vertex[1]); + st->add_index(p_polygon_vertex[1]); + st->add_index(p_polygon_vertex[0]); + return; + } else if (polygon_vertex_count == 3) { + // triangle to triangle + st->add_index(p_polygon_vertex[0]); + st->add_index(p_polygon_vertex[2]); + st->add_index(p_polygon_vertex[1]); + return; + } else if (polygon_vertex_count == 4) { + // quad to triangle - this code is awesome for import times + // it prevents triangles being generated slowly + st->add_index(p_polygon_vertex[0]); + st->add_index(p_polygon_vertex[2]); + st->add_index(p_polygon_vertex[1]); + st->add_index(p_polygon_vertex[2]); + st->add_index(p_polygon_vertex[0]); + st->add_index(p_polygon_vertex[3]); + return; + } else { + // non triangulated - we must run the triangulation algorithm + bool is_simple_convex = false; + // this code is 'slow' but required it triangulates all the unsupported geometry. + // Doesn't allow for bigger polygons because those are unlikely be convex + if (polygon_vertex_count <= 6) { + // Start from true, check if it's false. + is_simple_convex = true; + Vector3 first_vec; + for (int i = 0; i < polygon_vertex_count; i += 1) { + const Vector3 p1 = p_vertices[p_surface_vertex_map[p_polygon_vertex[i]]]; + const Vector3 p2 = p_vertices[p_surface_vertex_map[p_polygon_vertex[(i + 1) % polygon_vertex_count]]]; + const Vector3 p3 = p_vertices[p_surface_vertex_map[p_polygon_vertex[(i + 2) % polygon_vertex_count]]]; + + const Vector3 edge1 = p1 - p2; + const Vector3 edge2 = p3 - p2; + + const Vector3 res = edge1.normalized().cross(edge2.normalized()).normalized(); + if (i == 0) { + first_vec = res; + } else { + if (first_vec.dot(res) < 0.0) { + // Ok we found an angle that is not the same dir of the + // others. + is_simple_convex = false; + break; + } + } + } + } + + if (is_simple_convex) { + // This is a convex polygon, so just triangulate it. + for (int i = 0; i < (polygon_vertex_count - 2); i += 1) { + st->add_index(p_polygon_vertex[2 + i]); + st->add_index(p_polygon_vertex[1 + i]); + st->add_index(p_polygon_vertex[0]); + } + return; + } + } + + { + // This is a concave polygon. + + std::vector<Vector3> poly_vertices(polygon_vertex_count); + for (int i = 0; i < polygon_vertex_count; i += 1) { + poly_vertices[i] = p_vertices[p_surface_vertex_map[p_polygon_vertex[i]]]; + } + + const Vector3 poly_norm = get_poly_normal(poly_vertices); + if (poly_norm.length_squared() <= CMP_EPSILON) { + ERR_FAIL_COND_MSG(poly_norm.length_squared() <= CMP_EPSILON, "The normal of this poly was not computed. Is this FBX file corrupted."); + } + + // Select the plan coordinate. + int axis_1_coord = 0; + int axis_2_coord = 1; + { + real_t inv = poly_norm.z; + + const real_t axis_x = ABS(poly_norm.x); + const real_t axis_y = ABS(poly_norm.y); + const real_t axis_z = ABS(poly_norm.z); + + if (axis_x > axis_y) { + if (axis_x > axis_z) { + // For the most part the normal point toward X. + axis_1_coord = 1; + axis_2_coord = 2; + inv = poly_norm.x; + } + } else if (axis_y > axis_z) { + // For the most part the normal point toward Y. + axis_1_coord = 2; + axis_2_coord = 0; + inv = poly_norm.y; + } + + // Swap projection axes to take the negated projection vector into account + if (inv < 0.0f) { + SWAP(axis_1_coord, axis_2_coord); + } + } + + TriangulatorPoly triangulator_poly; + triangulator_poly.Init(polygon_vertex_count); + std::vector<Vector2> projected_vertices(polygon_vertex_count); + for (int i = 0; i < polygon_vertex_count; i += 1) { + const Vector2 pv(poly_vertices[i][axis_1_coord], poly_vertices[i][axis_2_coord]); + projected_vertices[i] = pv; + triangulator_poly.GetPoint(i) = pv; + } + triangulator_poly.SetOrientation(TRIANGULATOR_CCW); + + List<TriangulatorPoly> out_poly; + + TriangulatorPartition triangulator_partition; + if (triangulator_partition.Triangulate_OPT(&triangulator_poly, &out_poly) == 0) { // Good result. + if (triangulator_partition.Triangulate_EC(&triangulator_poly, &out_poly) == 0) { // Medium result. + if (triangulator_partition.Triangulate_MONO(&triangulator_poly, &out_poly) == 0) { // Really poor result. + ERR_FAIL_MSG("The triangulation of this polygon failed, please try to triangulate your mesh or check if it has broken polygons."); + } + } + } + + std::vector<Vector2> tris(out_poly.size()); + for (List<TriangulatorPoly>::Element *I = out_poly.front(); I; I = I->next()) { + TriangulatorPoly &tp = I->get(); + + ERR_FAIL_COND_MSG(tp.GetNumPoints() != 3, "The triangulator retuned more points, how this is possible?"); + // Find Index + for (int i = 2; i >= 0; i -= 1) { + const Vector2 vertex = tp.GetPoint(i); + bool done = false; + // Find Index + for (int y = 0; y < polygon_vertex_count; y += 1) { + if ((projected_vertices[y] - vertex).length_squared() <= CMP_EPSILON) { + // This seems the right vertex + st->add_index(p_polygon_vertex[y]); + done = true; + break; + } + } + ERR_FAIL_COND(done == false); + } + } + } +} + +void FBXMeshData::gen_weight_info(Ref<SurfaceTool> st, Vertex vertex_id) const { + if (vertex_weights.is_empty()) { + return; + } + + if (vertex_weights.has(vertex_id)) { + // Let's extract the weight info. + const VertexWeightMapping *vm = vertex_weights.getptr(vertex_id); + st->set_weights(vm->weights); + st->set_bones(vm->bones); + } +} + +int FBXMeshData::get_vertex_from_polygon_vertex(const std::vector<int> &p_polygon_indices, int p_index) const { + if (p_index < 0 || p_index >= (int)p_polygon_indices.size()) { + return -1; + } + + const int vertex = p_polygon_indices[p_index]; + if (vertex >= 0) { + return vertex; + } else { + // Negative numbers are the end of the face, reversing the bits is + // possible to obtain the positive correct vertex number. + return ~vertex; + } +} + +bool FBXMeshData::is_end_of_polygon(const std::vector<int> &p_polygon_indices, int p_index) const { + if (p_index < 0 || p_index >= (int)p_polygon_indices.size()) { + return false; + } + + const int vertex = p_polygon_indices[p_index]; + + // If the index is negative this is the end of the Polygon. + return vertex < 0; +} + +bool FBXMeshData::is_start_of_polygon(const std::vector<int> &p_polygon_indices, int p_index) const { + if (p_index < 0 || p_index >= (int)p_polygon_indices.size()) { + return false; + } + + if (p_index == 0) { + return true; + } + + // If the previous indices is negative this is the begin of a new Polygon. + return p_polygon_indices[p_index - 1] < 0; +} + +int FBXMeshData::count_polygons(const std::vector<int> &p_polygon_indices) const { + // The negative numbers define the end of the polygon. Counting the amount of + // negatives the numbers of polygons are obtained. + int count = 0; + for (size_t i = 0; i < p_polygon_indices.size(); i += 1) { + if (p_polygon_indices[i] < 0) { + count += 1; + } + } + return count; +} + +template <class R, class T> +HashMap<int, R> FBXMeshData::extract_per_vertex_data( + int p_vertex_count, + const std::vector<FBXDocParser::MeshGeometry::Edge> &p_edge_map, + const std::vector<int> &p_mesh_indices, + const FBXDocParser::MeshGeometry::MappingData<T> &p_mapping_data, + R (*collector_function)(const Vector<VertexData<T>> *p_vertex_data, R p_fall_back), + R p_fall_back) const { + /* When index_to_direct is set + * index size is 184 ( contains index for the data array [values 0, 96] ) + * data size is 96 (contains uv coordinates) + * this means index is simple data reduction basically + */ + //// + if (p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index_to_direct && p_mapping_data.index.size() == 0) { + print_verbose("debug count: index size: " + itos(p_mapping_data.index.size()) + ", data size: " + itos(p_mapping_data.data.size())); + print_verbose("vertex indices count: " + itos(p_mesh_indices.size())); + print_verbose("Edge map size: " + itos(p_edge_map.size())); + } + + ERR_FAIL_COND_V_MSG(p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index_to_direct && p_mapping_data.index.size() == 0, (HashMap<int, R>()), "FBX importer needs to map correctly to this field, please specify the override index name to fix this problem!"); + ERR_FAIL_COND_V_MSG(p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index && p_mapping_data.index.size() == 0, (HashMap<int, R>()), "The FBX seems corrupted"); + + // Aggregate vertex data. + HashMap<Vertex, Vector<VertexData<T>>> aggregate_vertex_data; + + switch (p_mapping_data.map_type) { + case FBXDocParser::MeshGeometry::MapType::none: { + // No data nothing to do. + return (HashMap<int, R>()); + } + case FBXDocParser::MeshGeometry::MapType::vertex: { + ERR_FAIL_COND_V_MSG(p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index_to_direct, (HashMap<int, R>()), "We will support in future"); + + if (p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::direct) { + // The data is mapped per vertex directly. + ERR_FAIL_COND_V_MSG((int)p_mapping_data.data.size() != p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR01"); + for (size_t vertex_index = 0; vertex_index < p_mapping_data.data.size(); vertex_index += 1) { + aggregate_vertex_data[vertex_index].push_back({ -1, p_mapping_data.data[vertex_index] }); + } + } else { + // The data is mapped per vertex using a reference. + // The indices array, contains a *reference_id for each vertex. + // * Note that the reference_id is the id of data into the data array. + // + // https://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_layer_element_html + ERR_FAIL_COND_V_MSG((int)p_mapping_data.index.size() != p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR02"); + for (size_t vertex_index = 0; vertex_index < p_mapping_data.index.size(); vertex_index += 1) { + ERR_FAIL_INDEX_V_MSG(p_mapping_data.index[vertex_index], (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR03."); + aggregate_vertex_data[vertex_index].push_back({ -1, p_mapping_data.data[p_mapping_data.index[vertex_index]] }); + } + } + } break; + case FBXDocParser::MeshGeometry::MapType::polygon_vertex: { + if (p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index_to_direct) { + // The data is mapped using each index from the indexes array then direct to the data (data reduction algorithm) + ERR_FAIL_COND_V_MSG((int)p_mesh_indices.size() != (int)p_mapping_data.index.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR04"); + int polygon_id = -1; + for (size_t polygon_vertex_index = 0; polygon_vertex_index < p_mapping_data.index.size(); polygon_vertex_index += 1) { + if (is_start_of_polygon(p_mesh_indices, polygon_vertex_index)) { + polygon_id += 1; + } + const int vertex_index = get_vertex_from_polygon_vertex(p_mesh_indices, polygon_vertex_index); + ERR_FAIL_COND_V_MSG(vertex_index < 0, (HashMap<int, R>()), "FBX file corrupted: #ERR05"); + ERR_FAIL_COND_V_MSG(vertex_index >= p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR06"); + const int index_to_direct = p_mapping_data.index[polygon_vertex_index]; + T value = p_mapping_data.data[index_to_direct]; + aggregate_vertex_data[vertex_index].push_back({ polygon_id, value }); + } + } else if (p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::direct) { + // The data are mapped per polygon vertex directly. + ERR_FAIL_COND_V_MSG((int)p_mesh_indices.size() != (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR04"); + int polygon_id = -1; + for (size_t polygon_vertex_index = 0; polygon_vertex_index < p_mapping_data.data.size(); polygon_vertex_index += 1) { + if (is_start_of_polygon(p_mesh_indices, polygon_vertex_index)) { + polygon_id += 1; + } + const int vertex_index = get_vertex_from_polygon_vertex(p_mesh_indices, polygon_vertex_index); + ERR_FAIL_COND_V_MSG(vertex_index < 0, (HashMap<int, R>()), "FBX file corrupted: #ERR05"); + ERR_FAIL_COND_V_MSG(vertex_index >= p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR06"); + + aggregate_vertex_data[vertex_index].push_back({ polygon_id, p_mapping_data.data[polygon_vertex_index] }); + } + } else { + // The data is mapped per polygon_vertex using a reference. + // The indices array, contains a *reference_id for each polygon_vertex. + // * Note that the reference_id is the id of data into the data array. + // + // https://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_layer_element_html + ERR_FAIL_COND_V_MSG(p_mesh_indices.size() != p_mapping_data.index.size(), (HashMap<int, R>()), "FBX file corrupted: #ERR7"); + int polygon_id = -1; + for (size_t polygon_vertex_index = 0; polygon_vertex_index < p_mapping_data.index.size(); polygon_vertex_index += 1) { + if (is_start_of_polygon(p_mesh_indices, polygon_vertex_index)) { + polygon_id += 1; + } + const int vertex_index = get_vertex_from_polygon_vertex(p_mesh_indices, polygon_vertex_index); + ERR_FAIL_COND_V_MSG(vertex_index < 0, (HashMap<int, R>()), "FBX file corrupted: #ERR8"); + ERR_FAIL_COND_V_MSG(vertex_index >= p_vertex_count, (HashMap<int, R>()), "FBX file seems corrupted: #ERR9."); + ERR_FAIL_COND_V_MSG(p_mapping_data.index[polygon_vertex_index] < 0, (HashMap<int, R>()), "FBX file seems corrupted: #ERR10."); + ERR_FAIL_COND_V_MSG(p_mapping_data.index[polygon_vertex_index] >= (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR11."); + aggregate_vertex_data[vertex_index].push_back({ polygon_id, p_mapping_data.data[p_mapping_data.index[polygon_vertex_index]] }); + } + } + } break; + case FBXDocParser::MeshGeometry::MapType::polygon: { + if (p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::direct) { + // The data are mapped per polygon directly. + const int polygon_count = count_polygons(p_mesh_indices); + ERR_FAIL_COND_V_MSG(polygon_count != (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR12"); + + // Advance each polygon vertex, each new polygon advance the polygon index. + int polygon_index = -1; + for (size_t polygon_vertex_index = 0; + polygon_vertex_index < p_mesh_indices.size(); + polygon_vertex_index += 1) { + if (is_start_of_polygon(p_mesh_indices, polygon_vertex_index)) { + polygon_index += 1; + ERR_FAIL_INDEX_V_MSG(polygon_index, (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR13"); + } + + const int vertex_index = get_vertex_from_polygon_vertex(p_mesh_indices, polygon_vertex_index); + ERR_FAIL_INDEX_V_MSG(vertex_index, p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR14"); + + aggregate_vertex_data[vertex_index].push_back({ polygon_index, p_mapping_data.data[polygon_index] }); + } + ERR_FAIL_COND_V_MSG((polygon_index + 1) != polygon_count, (HashMap<int, R>()), "FBX file seems corrupted: #ERR16. Not all Polygons are present in the file."); + } else { + // The data is mapped per polygon using a reference. + // The indices array, contains a *reference_id for each polygon. + // * Note that the reference_id is the id of data into the data array. + // + // https://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_layer_element_html + const int polygon_count = count_polygons(p_mesh_indices); + ERR_FAIL_COND_V_MSG(polygon_count != (int)p_mapping_data.index.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR17"); + + // Advance each polygon vertex, each new polygon advance the polygon index. + int polygon_index = -1; + for (size_t polygon_vertex_index = 0; + polygon_vertex_index < p_mesh_indices.size(); + polygon_vertex_index += 1) { + if (is_start_of_polygon(p_mesh_indices, polygon_vertex_index)) { + polygon_index += 1; + ERR_FAIL_INDEX_V_MSG(polygon_index, (int)p_mapping_data.index.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR18"); + ERR_FAIL_INDEX_V_MSG(p_mapping_data.index[polygon_index], (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR19"); + } + + const int vertex_index = get_vertex_from_polygon_vertex(p_mesh_indices, polygon_vertex_index); + ERR_FAIL_INDEX_V_MSG(vertex_index, p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR20"); + + aggregate_vertex_data[vertex_index].push_back({ polygon_index, p_mapping_data.data[p_mapping_data.index[polygon_index]] }); + } + ERR_FAIL_COND_V_MSG((polygon_index + 1) != polygon_count, (HashMap<int, R>()), "FBX file seems corrupted: #ERR22. Not all Polygons are present in the file."); + } + } break; + case FBXDocParser::MeshGeometry::MapType::edge: { + if (p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::direct) { + // The data are mapped per edge directly. + ERR_FAIL_COND_V_MSG(p_edge_map.size() != p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR23"); + for (size_t edge_index = 0; edge_index < p_mapping_data.data.size(); edge_index += 1) { + const FBXDocParser::MeshGeometry::Edge edge = FBXDocParser::MeshGeometry::get_edge(p_edge_map, edge_index); + ERR_FAIL_INDEX_V_MSG(edge.vertex_0, p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR24"); + ERR_FAIL_INDEX_V_MSG(edge.vertex_1, p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR25"); + ERR_FAIL_INDEX_V_MSG(edge.vertex_0, (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file corrupted: #ERR26"); + ERR_FAIL_INDEX_V_MSG(edge.vertex_1, (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file corrupted: #ERR27"); + aggregate_vertex_data[edge.vertex_0].push_back({ -1, p_mapping_data.data[edge_index] }); + aggregate_vertex_data[edge.vertex_1].push_back({ -1, p_mapping_data.data[edge_index] }); + } + } else { + // The data is mapped per edge using a reference. + // The indices array, contains a *reference_id for each polygon. + // * Note that the reference_id is the id of data into the data array. + // + // https://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_layer_element_html + ERR_FAIL_COND_V_MSG(p_edge_map.size() != p_mapping_data.index.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR28"); + for (size_t edge_index = 0; edge_index < p_mapping_data.data.size(); edge_index += 1) { + const FBXDocParser::MeshGeometry::Edge edge = FBXDocParser::MeshGeometry::get_edge(p_edge_map, edge_index); + ERR_FAIL_INDEX_V_MSG(edge.vertex_0, p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR29"); + ERR_FAIL_INDEX_V_MSG(edge.vertex_1, p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR30"); + ERR_FAIL_INDEX_V_MSG(edge.vertex_0, (int)p_mapping_data.index.size(), (HashMap<int, R>()), "FBX file corrupted: #ERR31"); + ERR_FAIL_INDEX_V_MSG(edge.vertex_1, (int)p_mapping_data.index.size(), (HashMap<int, R>()), "FBX file corrupted: #ERR32"); + ERR_FAIL_INDEX_V_MSG(p_mapping_data.index[edge.vertex_0], (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file corrupted: #ERR33"); + ERR_FAIL_INDEX_V_MSG(p_mapping_data.index[edge.vertex_1], (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file corrupted: #ERR34"); + aggregate_vertex_data[edge.vertex_0].push_back({ -1, p_mapping_data.data[p_mapping_data.index[edge_index]] }); + aggregate_vertex_data[edge.vertex_1].push_back({ -1, p_mapping_data.data[p_mapping_data.index[edge_index]] }); + } + } + } break; + case FBXDocParser::MeshGeometry::MapType::all_the_same: { + // No matter the mode, no matter the data size; The first always win + // and is set to all the vertices. + ERR_FAIL_COND_V_MSG(p_mapping_data.data.size() <= 0, (HashMap<int, R>()), "FBX file seems corrupted: #ERR35"); + if (p_mapping_data.data.size() > 0) { + for (int vertex_index = 0; vertex_index < p_vertex_count; vertex_index += 1) { + aggregate_vertex_data[vertex_index].push_back({ -1, p_mapping_data.data[0] }); + } + } + } break; + } + + if (aggregate_vertex_data.size() == 0) { + return (HashMap<int, R>()); + } + + // A map is used because turns out that the some FBX file are not well organized + // with vertices well compacted. Using a map allows avoid those issues. + HashMap<Vertex, R> result; + + // Aggregate the collected data. + for (const Vertex *index = aggregate_vertex_data.next(nullptr); index != nullptr; index = aggregate_vertex_data.next(index)) { + Vector<VertexData<T>> *aggregated_vertex = aggregate_vertex_data.getptr(*index); + // This can't be null because we are just iterating. + CRASH_COND(aggregated_vertex == nullptr); + + ERR_FAIL_INDEX_V_MSG(0, aggregated_vertex->size(), (HashMap<int, R>()), "The FBX file is corrupted, No valid data for this vertex index."); + result[*index] = collector_function(aggregated_vertex, p_fall_back); + } + + // Sanitize the data now, if the file is broken we can try import it anyway. + bool problem_found = false; + for (size_t i = 0; i < p_mesh_indices.size(); i += 1) { + const Vertex vertex = get_vertex_from_polygon_vertex(p_mesh_indices, i); + if (result.has(vertex) == false) { + result[vertex] = p_fall_back; + problem_found = true; + } + } + if (problem_found) { + WARN_PRINT("Some data is missing, this FBX file may be corrupted: #WARN0."); + } + + return result; +} + +template <class T> +HashMap<int, T> FBXMeshData::extract_per_polygon( + int p_vertex_count, + const std::vector<int> &p_polygon_indices, + const FBXDocParser::MeshGeometry::MappingData<T> &p_fbx_data, + T p_fallback_value) const { + ERR_FAIL_COND_V_MSG(p_fbx_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index_to_direct && p_fbx_data.data.size() == 0, (HashMap<int, T>()), "invalid index to direct array"); + ERR_FAIL_COND_V_MSG(p_fbx_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index && p_fbx_data.index.size() == 0, (HashMap<int, T>()), "The FBX seems corrupted"); + + const int polygon_count = count_polygons(p_polygon_indices); + + // Aggregate vertex data. + HashMap<int, Vector<T>> aggregate_polygon_data; + + switch (p_fbx_data.map_type) { + case FBXDocParser::MeshGeometry::MapType::none: { + // No data nothing to do. + return (HashMap<int, T>()); + } + case FBXDocParser::MeshGeometry::MapType::vertex: { + ERR_FAIL_V_MSG((HashMap<int, T>()), "This data can't be extracted and organized per polygon, since into the FBX is mapped per vertex. This should not happen."); + } break; + case FBXDocParser::MeshGeometry::MapType::polygon_vertex: { + ERR_FAIL_V_MSG((HashMap<int, T>()), "This data can't be extracted and organized per polygon, since into the FBX is mapped per polygon vertex. This should not happen."); + } break; + case FBXDocParser::MeshGeometry::MapType::polygon: { + if (p_fbx_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index_to_direct) { + // The data is stored efficiently index_to_direct allows less data in the FBX file. + for (int polygon_index = 0; + polygon_index < polygon_count; + polygon_index += 1) { + if (p_fbx_data.index.size() == 0) { + ERR_FAIL_INDEX_V_MSG(polygon_index, (int)p_fbx_data.data.size(), (HashMap<int, T>()), "FBX file is corrupted: #ERR62"); + aggregate_polygon_data[polygon_index].push_back(p_fbx_data.data[polygon_index]); + } else { + ERR_FAIL_INDEX_V_MSG(polygon_index, (int)p_fbx_data.index.size(), (HashMap<int, T>()), "FBX file is corrupted: #ERR62"); + + const int index_to_direct = p_fbx_data.index[polygon_index]; + T value = p_fbx_data.data[index_to_direct]; + aggregate_polygon_data[polygon_index].push_back(value); + } + } + } else if (p_fbx_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::direct) { + // The data are mapped per polygon directly. + ERR_FAIL_COND_V_MSG(polygon_count != (int)p_fbx_data.data.size(), (HashMap<int, T>()), "FBX file is corrupted: #ERR51"); + + // Advance each polygon vertex, each new polygon advance the polygon index. + for (int polygon_index = 0; + polygon_index < polygon_count; + polygon_index += 1) { + ERR_FAIL_INDEX_V_MSG(polygon_index, (int)p_fbx_data.data.size(), (HashMap<int, T>()), "FBX file is corrupted: #ERR52"); + aggregate_polygon_data[polygon_index].push_back(p_fbx_data.data[polygon_index]); + } + } else { + // The data is mapped per polygon using a reference. + // The indices array, contains a *reference_id for each polygon. + // * Note that the reference_id is the id of data into the data array. + // + // https://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_layer_element_html + ERR_FAIL_COND_V_MSG(polygon_count != (int)p_fbx_data.index.size(), (HashMap<int, T>()), "FBX file seems corrupted: #ERR52"); + + // Advance each polygon vertex, each new polygon advance the polygon index. + for (int polygon_index = 0; + polygon_index < polygon_count; + polygon_index += 1) { + ERR_FAIL_INDEX_V_MSG(polygon_index, (int)p_fbx_data.index.size(), (HashMap<int, T>()), "FBX file is corrupted: #ERR53"); + ERR_FAIL_INDEX_V_MSG(p_fbx_data.index[polygon_index], (int)p_fbx_data.data.size(), (HashMap<int, T>()), "FBX file is corrupted: #ERR54"); + aggregate_polygon_data[polygon_index].push_back(p_fbx_data.data[p_fbx_data.index[polygon_index]]); + } + } + } break; + case FBXDocParser::MeshGeometry::MapType::edge: { + ERR_FAIL_V_MSG((HashMap<int, T>()), "This data can't be extracted and organized per polygon, since into the FBX is mapped per edge. This should not happen."); + } break; + case FBXDocParser::MeshGeometry::MapType::all_the_same: { + // No matter the mode, no matter the data size; The first always win + // and is set to all the vertices. + ERR_FAIL_COND_V_MSG(p_fbx_data.data.size() <= 0, (HashMap<int, T>()), "FBX file seems corrupted: #ERR55"); + if (p_fbx_data.data.size() > 0) { + for (int polygon_index = 0; polygon_index < polygon_count; polygon_index += 1) { + aggregate_polygon_data[polygon_index].push_back(p_fbx_data.data[0]); + } + } + } break; + } + + if (aggregate_polygon_data.size() == 0) { + return (HashMap<int, T>()); + } + + // A map is used because turns out that the some FBX file are not well organized + // with vertices well compacted. Using a map allows avoid those issues. + HashMap<int, T> polygons; + + // Take the first value for each vertex. + for (const Vertex *index = aggregate_polygon_data.next(nullptr); index != nullptr; index = aggregate_polygon_data.next(index)) { + Vector<T> *aggregated_polygon = aggregate_polygon_data.getptr(*index); + // This can't be null because we are just iterating. + CRASH_COND(aggregated_polygon == nullptr); + + ERR_FAIL_INDEX_V_MSG(0, (int)aggregated_polygon->size(), (HashMap<int, T>()), "The FBX file is corrupted, No valid data for this polygon index."); + + // Validate the final value. + polygons[*index] = (*aggregated_polygon)[0]; + } + + // Sanitize the data now, if the file is broken we can try import it anyway. + bool problem_found = false; + for (int polygon_i = 0; polygon_i < polygon_count; polygon_i += 1) { + if (polygons.has(polygon_i) == false) { + polygons[polygon_i] = p_fallback_value; + problem_found = true; + } + } + if (problem_found) { + WARN_PRINT("Some data is missing, this FBX file may be corrupted: #WARN1."); + } + + return polygons; +} + +void FBXMeshData::extract_morphs(const FBXDocParser::MeshGeometry *mesh_geometry, HashMap<String, MorphVertexData> &r_data) { + r_data.clear(); + + const int vertex_count = mesh_geometry->get_vertices().size(); + + for (const FBXDocParser::BlendShape *blend_shape : mesh_geometry->get_blend_shapes()) { + for (const FBXDocParser::BlendShapeChannel *blend_shape_channel : blend_shape->BlendShapeChannels()) { + const std::vector<const FBXDocParser::ShapeGeometry *> &shape_geometries = blend_shape_channel->GetShapeGeometries(); + for (const FBXDocParser::ShapeGeometry *shape_geometry : shape_geometries) { + String morph_name = ImportUtils::FBXAnimMeshName(shape_geometry->Name()).c_str(); + if (morph_name.is_empty()) { + morph_name = "morph"; + } + + // TODO we have only these?? + const std::vector<unsigned int> &morphs_vertex_indices = shape_geometry->GetIndices(); + const std::vector<Vector3> &morphs_vertices = shape_geometry->GetVertices(); + const std::vector<Vector3> &morphs_normals = shape_geometry->GetNormals(); + + ERR_FAIL_COND_MSG((int)morphs_vertex_indices.size() > vertex_count, "The FBX file is corrupted: #ERR103"); + ERR_FAIL_COND_MSG(morphs_vertex_indices.size() != morphs_vertices.size(), "The FBX file is corrupted: #ERR104"); + ERR_FAIL_COND_MSG((int)morphs_vertices.size() > vertex_count, "The FBX file is corrupted: #ERR105"); + ERR_FAIL_COND_MSG(morphs_normals.size() != 0 && morphs_normals.size() != morphs_vertices.size(), "The FBX file is corrupted: #ERR106"); + + if (r_data.has(morph_name) == false) { + // This morph doesn't exist yet. + // Create it. + MorphVertexData md; + md.vertices.resize(vertex_count); + md.normals.resize(vertex_count); + r_data.set(morph_name, md); + } + + MorphVertexData *data = r_data.getptr(morph_name); + Vector3 *data_vertices_ptr = data->vertices.ptrw(); + Vector3 *data_normals_ptr = data->normals.ptrw(); + + for (int i = 0; i < (int)morphs_vertex_indices.size(); i += 1) { + const Vertex vertex = morphs_vertex_indices[i]; + + ERR_FAIL_INDEX_MSG(vertex, vertex_count, "The blend shapes of this FBX file are corrupted. It has a not valid vertex."); + + data_vertices_ptr[vertex] = morphs_vertices[i]; + + if (morphs_normals.size() != 0) { + data_normals_ptr[vertex] = morphs_normals[i]; + } + } + } + } + } +} diff --git a/modules/fbx/data/fbx_mesh_data.h b/modules/fbx/data/fbx_mesh_data.h new file mode 100644 index 0000000000..77510ff2ec --- /dev/null +++ b/modules/fbx/data/fbx_mesh_data.h @@ -0,0 +1,184 @@ +/*************************************************************************/ +/* fbx_mesh_data.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef FBX_MESH_DATA_H +#define FBX_MESH_DATA_H + +#include "core/templates/hash_map.h" +#include "editor/import/resource_importer_scene.h" +#include "editor/import/scene_importer_mesh_node_3d.h" +#include "scene/3d/mesh_instance_3d.h" +#include "scene/resources/surface_tool.h" + +#include "fbx_bone.h" +#include "fbx_parser/FBXMeshGeometry.h" +#include "import_state.h" +#include "tools/import_utils.h" + +struct FBXNode; +struct FBXMeshData; +struct FBXBone; +struct ImportState; + +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; + + EditorSceneImporterMeshNode3D *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; + EditorSceneImporterMeshNode3D *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 diff --git a/modules/fbx/data/fbx_node.h b/modules/fbx/data/fbx_node.h new file mode 100644 index 0000000000..a6f62f3388 --- /dev/null +++ b/modules/fbx/data/fbx_node.h @@ -0,0 +1,63 @@ +/*************************************************************************/ +/* fbx_node.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef FBX_NODE_H +#define FBX_NODE_H + +#include "fbx_skeleton.h" +#include "model_abstraction.h" +#include "pivot_transform.h" + +#include "fbx_parser/FBXDocument.h" + +class Node3D; +struct PivotTransform; + +struct FBXNode : 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 diff --git a/modules/fbx/data/fbx_skeleton.cpp b/modules/fbx/data/fbx_skeleton.cpp new file mode 100644 index 0000000000..622b589feb --- /dev/null +++ b/modules/fbx/data/fbx_skeleton.cpp @@ -0,0 +1,123 @@ +/*************************************************************************/ +/* fbx_skeleton.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "fbx_skeleton.h" + +#include "import_state.h" + +#include "tools/import_utils.h" + +void FBXSkeleton::init_skeleton(const ImportState &state) { + int skeleton_bone_count = skeleton_bones.size(); + + if (skeleton == nullptr && skeleton_bone_count > 0) { + skeleton = memnew(Skeleton3D); + + if (fbx_node.is_valid()) { + // cache skeleton attachment for later during node creation + // can't be done until after node hierarchy is built + if (fbx_node->godot_node != state.root) { + fbx_node->skeleton_node = Ref<FBXSkeleton>(this); + print_verbose("cached armature skeleton attachment for node " + fbx_node->node_name); + } else { + // root node must never be a skeleton to prevent cyclic skeletons from being allowed (skeleton in a skeleton) + fbx_node->godot_node->add_child(skeleton); + skeleton->set_owner(state.root_owner); + skeleton->set_name("Skeleton3D"); + print_verbose("created armature skeleton for root"); + } + } else { + memfree(skeleton); + skeleton = nullptr; + print_error("[doc] skeleton has no valid node to parent nodes to - erasing"); + skeleton_bones.clear(); + return; + } + } + + // Make the bone name uniques. + for (int x = 0; x < skeleton_bone_count; x++) { + Ref<FBXBone> bone = skeleton_bones[x]; + if (bone.is_valid()) { + // Make sure the bone name is unique. + const String bone_name = bone->bone_name; + int same_name_count = 0; + for (int y = x; 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 + } + } + } +} diff --git a/modules/fbx/data/fbx_skeleton.h b/modules/fbx/data/fbx_skeleton.h new file mode 100644 index 0000000000..df937cde49 --- /dev/null +++ b/modules/fbx/data/fbx_skeleton.h @@ -0,0 +1,53 @@ +/*************************************************************************/ +/* fbx_skeleton.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef FBX_SKELETON_H +#define FBX_SKELETON_H + +#include "fbx_bone.h" +#include "fbx_node.h" +#include "model_abstraction.h" + +#include "core/object/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 diff --git a/modules/fbx/data/import_state.h b/modules/fbx/data/import_state.h new file mode 100644 index 0000000000..83bc43faff --- /dev/null +++ b/modules/fbx/data/import_state.h @@ -0,0 +1,113 @@ +/*************************************************************************/ +/* import_state.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef IMPORT_STATE_H +#define IMPORT_STATE_H + +#include "fbx_mesh_data.h" +#include "tools/import_utils.h" +#include "tools/validation_tools.h" + +#include "pivot_transform.h" + +#include "core/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; + bool is_blender_fbx = false; + + Map<StringName, Ref<Texture>> cached_image_searches; + Map<uint64_t, Ref<Material>> cached_materials; + + String path = String(); + Node3D *root_owner = nullptr; + Node3D *root = nullptr; + real_t scale = 0.01; + Ref<FBXNode> fbx_root_node = Ref<FBXNode>(); + // skeleton map - merged automatically when they are on the same x node in the tree so we can merge them automatically. + Map<uint64_t, Ref<FBXSkeleton>> skeleton_map = Map<uint64_t, Ref<FBXSkeleton>>(); + + // nodes on the same level get merged automatically. + //Map<uint64_t, Skeleton3D *> armature_map; + AnimationPlayer *animation_player = nullptr; + + // Generation 4 - Raw document accessing for bone/skin/joint/kLocators + // joints are not necessarily bones but must be merged into the skeleton + // (bone id), bone + Map<uint64_t, Ref<FBXBone>> fbx_bone_map = Map<uint64_t, Ref<FBXBone>>(); // this is the bone name and setup information required for joints + // this will never contain joints only bones attached to a mesh. + + // Generation 4 - Raw document for creating the nodes transforms in the scene + // this is a list of the nodes in the scene + // (id, node) + List<Ref<FBXNode>> fbx_node_list = List<Ref<FBXNode>>(); + + // All nodes which have been created in the scene + // this will not contain the root node of the scene + Map<uint64_t, Ref<FBXNode>> fbx_target_map = Map<uint64_t, Ref<FBXNode>>(); + + // mesh nodes which are created in node / mesh step - used for populating skin poses in MeshSkins + Map<uint64_t, Ref<FBXNode>> MeshNodes = Map<uint64_t, Ref<FBXNode>>(); + // mesh skin map + Map<uint64_t, Ref<Skin>> MeshSkins = Map<uint64_t, Ref<Skin>>(); + + // this is the container for the mesh weight information and eventually + // any mesh data + // but not the skin, just stuff important for rendering + // skin is applied to mesh instance so not really required to be in here yet. + // maybe later + // fbx mesh id, FBXMeshData + Map<uint64_t, Ref<FBXMeshData>> renderer_mesh_data = Map<uint64_t, Ref<FBXMeshData>>(); +}; + +#endif // IMPORT_STATE_H diff --git a/modules/fbx/data/model_abstraction.h b/modules/fbx/data/model_abstraction.h new file mode 100644 index 0000000000..528960ab49 --- /dev/null +++ b/modules/fbx/data/model_abstraction.h @@ -0,0 +1,52 @@ +/*************************************************************************/ +/* model_abstraction.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef MODEL_ABSTRACTION_H +#define MODEL_ABSTRACTION_H + +#include "modules/fbx/fbx_parser/FBXDocument.h" + +struct ModelAbstraction { + mutable const FBXDocParser::Model *fbx_model = nullptr; + + void set_model(const FBXDocParser::Model *p_model) { + fbx_model = p_model; + } + + bool has_model() const { + return fbx_model != nullptr; + } + + const FBXDocParser::Model *get_model() const { + return fbx_model; + } +}; + +#endif // MODEL_ABSTRACTION_H diff --git a/modules/fbx/data/pivot_transform.cpp b/modules/fbx/data/pivot_transform.cpp new file mode 100644 index 0000000000..7a56074bc5 --- /dev/null +++ b/modules/fbx/data/pivot_transform.cpp @@ -0,0 +1,294 @@ +/*************************************************************************/ +/* pivot_transform.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "pivot_transform.h" + +#include "tools/import_utils.h" + +void PivotTransform::ReadTransformChain() { + const FBXDocParser::PropertyTable *props = fbx_model->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; +} diff --git a/modules/fbx/data/pivot_transform.h b/modules/fbx/data/pivot_transform.h new file mode 100644 index 0000000000..9996027870 --- /dev/null +++ b/modules/fbx/data/pivot_transform.h @@ -0,0 +1,115 @@ +/*************************************************************************/ +/* pivot_transform.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef PIVOT_TRANSFORM_H +#define PIVOT_TRANSFORM_H + +#include "core/math/transform.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 diff --git a/modules/fbx/doc_classes/EditorSceneImporterFBX.xml b/modules/fbx/doc_classes/EditorSceneImporterFBX.xml new file mode 100644 index 0000000000..da1a68c27c --- /dev/null +++ b/modules/fbx/doc_classes/EditorSceneImporterFBX.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="EditorSceneImporterFBX" inherits="EditorSceneImporter" version="4.0"> + <brief_description> + FBX 3D asset importer. + </brief_description> + <description> + This is an FBX 3D asset importer with full support for most FBX features. + If exporting a FBX scene from Autodesk Maya, use these FBX export settings: + [codeblock] + - Smoothing Groups + - Smooth Mesh + - Triangluate (for meshes with blend shapes) + - Bake Animation + - Resample All + - Deformed Models + - Skins + - Blend Shapes + - Curve Filters + - Constant Key Reducer + - Auto Tangents Only + - *Do not check* Constraints (as it will break the file) + - Can check Embed Media (embeds textures into the exported FBX file) + - Note that when importing embedded media, the texture and mesh will be a single immutable file. + - You will have to re-export then re-import the FBX if the texture has changed. + - Units: Centimeters + - Up Axis: Y + - Binary format in FBX 2017 + [/codeblock] + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/modules/fbx/editor_scene_importer_fbx.cpp b/modules/fbx/editor_scene_importer_fbx.cpp new file mode 100644 index 0000000000..6576147b2b --- /dev/null +++ b/modules/fbx/editor_scene_importer_fbx.cpp @@ -0,0 +1,1427 @@ +/*************************************************************************/ +/* editor_scene_importer_fbx.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "editor_scene_importer_fbx.h" + +#include "data/fbx_anim_container.h" +#include "data/fbx_material.h" +#include "data/fbx_mesh_data.h" +#include "data/fbx_skeleton.h" +#include "tools/import_utils.h" + +#include "core/io/image_loader.h" +#include "editor/editor_log.h" +#include "editor/editor_node.h" +#include "editor/import/resource_importer_scene.h" +#include "editor/import/scene_importer_mesh_node_3d.h" +#include "scene/3d/bone_attachment_3d.h" +#include "scene/3d/camera_3d.h" +#include "scene/3d/light_3d.h" +#include "scene/3d/mesh_instance_3d.h" +#include "scene/main/node.h" +#include "scene/resources/material.h" + +#include "fbx_parser/FBXDocument.h" +#include "fbx_parser/FBXImportSettings.h" +#include "fbx_parser/FBXMeshGeometry.h" +#include "fbx_parser/FBXParser.h" +#include "fbx_parser/FBXProperties.h" +#include "fbx_parser/FBXTokenizer.h" + +#include <string> + +void EditorSceneImporterFBX::get_extensions(List<String> *r_extensions) const { + // register FBX as the one and only format for FBX importing + const String import_setting_string = "filesystem/import/fbx/"; + const String fbx_str = "fbx"; + Vector<String> exts; + exts.push_back(fbx_str); + _register_project_setting_import(fbx_str, import_setting_string, exts, r_extensions, true); +} + +void EditorSceneImporterFBX::_register_project_setting_import(const String generic, + const String import_setting_string, + const Vector<String> &exts, + List<String> *r_extensions, + const bool p_enabled) const { + const String use_generic = "use_" + generic; + _GLOBAL_DEF(import_setting_string + use_generic, p_enabled, true); + if (ProjectSettings::get_singleton()->get(import_setting_string + use_generic)) { + for (int32_t i = 0; i < exts.size(); i++) { + r_extensions->push_back(exts[i]); + } + } +} + +uint32_t EditorSceneImporterFBX::get_import_flags() const { + return IMPORT_SCENE; +} + +Node3D *EditorSceneImporterFBX::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, + List<String> *r_missing_deps, Error *r_err) { + // done for performance when re-importing lots of files when testing importer in verbose only! + if (OS::get_singleton()->is_stdout_verbose()) { + EditorLog *log = EditorNode::get_log(); + log->clear(); + } + Error err; + FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err); + + ERR_FAIL_COND_V(!f, NULL); + + { + PackedByteArray data; + // broadphase tokenizing pass in which we identify the core + // syntax elements of FBX (brackets, commas, key:value mappings) + FBXDocParser::TokenList tokens; + + bool is_binary = false; + data.resize(f->get_len()); + f->get_buffer(data.ptrw(), data.size()); + PackedByteArray fbx_header; + fbx_header.resize(64); + for (int32_t byte_i = 0; byte_i < 64; byte_i++) { + fbx_header.ptrw()[byte_i] = data.ptr()[byte_i]; + } + + String fbx_header_string; + if (fbx_header.size() >= 0) { + fbx_header_string.parse_utf8((const char *)fbx_header.ptr(), fbx_header.size()); + } + + print_verbose("[doc] opening fbx file: " + p_path); + print_verbose("[doc] fbx header: " + fbx_header_string); + + // safer to check this way as there can be different formatted headers + if (fbx_header_string.find("Kaydara FBX Binary", 0) != -1) { + is_binary = true; + print_verbose("[doc] is binary"); + FBXDocParser::TokenizeBinary(tokens, (const char *)data.ptrw(), (size_t)data.size()); + } else { + print_verbose("[doc] is ascii"); + FBXDocParser::Tokenize(tokens, (const char *)data.ptrw(), (size_t)data.size()); + } + + // The import process explained: + // 1. Tokens are made, these are then taken into the 'parser' below + // 2. The parser constructs 'Elements' and all 'real' FBX Types. + // 3. This creates a problem: shared_ptr ownership, should Elements later 'take ownership' + // 4. No, it shouldn't so we should either a.) use weak ref for elements; but this is not correct. + + // use this information to construct a very rudimentary + // parse-tree representing the FBX scope structure + FBXDocParser::Parser parser(tokens, is_binary); + FBXDocParser::ImportSettings settings; + settings.strictMode = false; + + // this function leaks a lot + FBXDocParser::Document doc(parser, settings); + + // yeah so closing the file is a good idea (prevents readonly states) + f->close(); + + // safety for version handling + if (doc.IsSafeToImport()) { + bool is_blender_fbx = false; + //const FBXDocParser::PropertyPtr app_vendor = p_document->GlobalSettingsPtr()->Props() + // p_document->Creator() + const FBXDocParser::PropertyTable *import_props = doc.GetMetadataProperties(); + const FBXDocParser::PropertyPtr app_name = import_props->Get("Original|ApplicationName"); + const FBXDocParser::PropertyPtr app_vendor = import_props->Get("Original|ApplicationVendor"); + const FBXDocParser::PropertyPtr app_version = import_props->Get("Original|ApplicationVersion"); + // + if (app_name) { + const FBXDocParser::TypedProperty<std::string> *app_name_string = dynamic_cast<const FBXDocParser::TypedProperty<std::string> *>(app_name); + if (app_name_string) { + print_verbose("FBX App Name: " + String(app_name_string->Value().c_str())); + } + } + + if (app_vendor) { + const FBXDocParser::TypedProperty<std::string> *app_vendor_string = dynamic_cast<const FBXDocParser::TypedProperty<std::string> *>(app_vendor); + if (app_vendor_string) { + print_verbose("FBX App Vendor: " + String(app_vendor_string->Value().c_str())); + is_blender_fbx = app_vendor_string->Value().find("Blender") != std::string::npos; + } + } + + if (app_version) { + const FBXDocParser::TypedProperty<std::string> *app_version_string = dynamic_cast<const FBXDocParser::TypedProperty<std::string> *>(app_version); + if (app_version_string) { + print_verbose("FBX App Version: " + String(app_version_string->Value().c_str())); + } + } + + if (is_blender_fbx) { + WARN_PRINT("We don't officially support Blender FBX animations yet, due to issues with upstream Blender,\n" + "so please wait for us to work around remaining issues. We will continue to import the file but it may be broken.\n" + "For minimal breakage, please export FBX from Blender with -Z forward, and Y up."); + } + + Node3D *spatial = _generate_scene(p_path, &doc, p_flags, p_bake_fps, 8, is_blender_fbx); + // todo: move to document shutdown (will need to be validated after moving; this code has been validated already) + for (FBXDocParser::TokenPtr token : tokens) { + if (token) { + delete token; + token = nullptr; + } + } + + return spatial; + + } else { + ERR_PRINT(vformat("Cannot import FBX file: %s. It uses file format %d which is unsupported by Godot. Please re-export it or convert it to a newer format.", p_path, doc.FBXVersion())); + } + } + + return memnew(Node3D); +} + +template <class T> +struct EditorSceneImporterAssetImportInterpolate { + T lerp(const T &a, const T &b, float c) const { + return a + (b - a) * c; + } + + T catmull_rom(const T &p0, const T &p1, const T &p2, const T &p3, float t) { + const float t2 = t * t; + const float t3 = t2 * t; + + return 0.5f * ((2.0f * p1) + (-p0 + p2) * t + (2.0f * p0 - 5.0f * p1 + 4.0f * p2 - p3) * t2 + (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3); + } + + T bezier(T start, T control_1, T control_2, T end, float t) { + /* Formula from Wikipedia article on Bezier curves. */ + const real_t omt = (1.0 - t); + const real_t omt2 = omt * omt; + const real_t omt3 = omt2 * omt; + const real_t t2 = t * t; + const real_t t3 = t2 * t; + + return start * omt3 + control_1 * omt2 * t * 3.0 + control_2 * omt * t2 * 3.0 + end * t3; + } +}; + +//thank you for existing, partial specialization +template <> +struct EditorSceneImporterAssetImportInterpolate<Quat> { + Quat lerp(const Quat &a, const Quat &b, float c) const { + ERR_FAIL_COND_V(!a.is_normalized(), Quat()); + ERR_FAIL_COND_V(!b.is_normalized(), Quat()); + + return a.slerp(b, c).normalized(); + } + + Quat catmull_rom(const Quat &p0, const Quat &p1, const Quat &p2, const Quat &p3, float c) { + ERR_FAIL_COND_V(!p1.is_normalized(), Quat()); + ERR_FAIL_COND_V(!p2.is_normalized(), Quat()); + + return p1.slerp(p2, c).normalized(); + } + + Quat bezier(Quat start, Quat control_1, Quat control_2, Quat end, float t) { + ERR_FAIL_COND_V(!start.is_normalized(), Quat()); + ERR_FAIL_COND_V(!end.is_normalized(), Quat()); + + return start.slerp(end, t).normalized(); + } +}; + +template <class T> +T EditorSceneImporterFBX::_interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time, + AssetImportAnimation::Interpolation p_interp) { + //could use binary search, worth it? + int idx = -1; + for (int i = 0; i < p_times.size(); i++) { + if (p_times[i] > p_time) + break; + idx++; + } + + EditorSceneImporterAssetImportInterpolate<T> interp; + + switch (p_interp) { + case AssetImportAnimation::INTERP_LINEAR: { + if (idx == -1) { + return p_values[0]; + } else if (idx >= p_times.size() - 1) { + return p_values[p_times.size() - 1]; + } + + float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]); + + return interp.lerp(p_values[idx], p_values[idx + 1], c); + + } break; + case AssetImportAnimation::INTERP_STEP: { + if (idx == -1) { + return p_values[0]; + } else if (idx >= p_times.size() - 1) { + return p_values[p_times.size() - 1]; + } + + return p_values[idx]; + + } break; + case AssetImportAnimation::INTERP_CATMULLROMSPLINE: { + if (idx == -1) { + return p_values[1]; + } else if (idx >= p_times.size() - 1) { + return p_values[1 + p_times.size() - 1]; + } + + float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]); + + return interp.catmull_rom(p_values[idx - 1], p_values[idx], p_values[idx + 1], p_values[idx + 3], c); + + } break; + case AssetImportAnimation::INTERP_CUBIC_SPLINE: { + if (idx == -1) { + return p_values[1]; + } else if (idx >= p_times.size() - 1) { + return p_values[(p_times.size() - 1) * 3 + 1]; + } + + float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]); + + T from = p_values[idx * 3 + 1]; + T c1 = from + p_values[idx * 3 + 2]; + T to = p_values[idx * 3 + 4]; + T c2 = to + p_values[idx * 3 + 3]; + + return interp.bezier(from, c1, c2, to, c); + + } break; + } + + ERR_FAIL_V(p_values[0]); +} + +Node3D *EditorSceneImporterFBX::_generate_scene( + const String &p_path, + const FBXDocParser::Document *p_document, + const uint32_t p_flags, + int p_bake_fps, + const int32_t p_max_bone_weights, + bool p_is_blender_fbx) { + ImportState state; + state.is_blender_fbx = p_is_blender_fbx; + state.path = p_path; + state.animation_player = NULL; + + // create new root node for scene + Node3D *scene_root = memnew(Node3D); + state.root = memnew(Node3D); + state.root_owner = scene_root; // the real scene root... sorry compatibility code is painful... + + state.root->set_name("RootNode"); + scene_root->add_child(state.root); + state.root->set_owner(scene_root); + + state.fbx_root_node.instance(); + state.fbx_root_node->godot_node = state.root; + + // Size relative to cm. + const real_t fbx_unit_scale = p_document->GlobalSettingsPtr()->UnitScaleFactor(); + + print_verbose("FBX unit scale import value: " + rtos(fbx_unit_scale)); + // Set FBX file scale is relative to CM must be converted to M + state.scale = fbx_unit_scale / 100.0; + print_verbose("FBX unit scale is: " + rtos(state.scale)); + + // Enabled by default. + state.enable_material_import = true; + // Enabled by default. + state.enable_animation_import = true; + Ref<FBXNode> root_node; + root_node.instance(); + + // make sure fake noFBXDocParser::PropertyPtr ptrde always has a transform too ;) + Ref<PivotTransform> pivot_transform; + pivot_transform.instance(); + root_node->pivot_transform = pivot_transform; + root_node->node_name = "root node"; + root_node->current_node_id = 0; + root_node->godot_node = state.root; + + // cache this node onto the fbx_target map. + state.fbx_target_map.insert(0, root_node); + + // cache basic node information from FBX document + // grabs all FBX bones + BuildDocumentBones(Ref<FBXBone>(), state, p_document, 0L); + BuildDocumentNodes(Ref<PivotTransform>(), state, p_document, 0L, nullptr); + + // Build document skinning information + + // Algorithm is this: + // Get Deformer: object with "Skin" class. + // Deformer:: has link to Geometry:: (correct mesh for skin) + // Deformer:: has Source which is the SubDeformer:: (e.g. the Cluster) + // Notes at the end it configures the vertex weight mapping. + + for (uint64_t skin_id : p_document->GetSkinIDs()) { + // Validate the parser + FBXDocParser::LazyObject *lazy_skin = p_document->GetObject(skin_id); + ERR_CONTINUE_MSG(lazy_skin == nullptr, "invalid lazy object [serious parser bug]"); + + // Validate the parser + const FBXDocParser::Skin *skin = lazy_skin->Get<FBXDocParser::Skin>(); + ERR_CONTINUE_MSG(skin == nullptr, "invalid skin added to skin list [parser bug]"); + + const std::vector<const FBXDocParser::Connection *> source_to_destination = p_document->GetConnectionsBySourceSequenced(skin_id); + FBXDocParser::MeshGeometry *mesh = nullptr; + uint64_t mesh_id = 0; + + // Most likely only contains the mesh link for the skin + // The mesh geometry. + for (const FBXDocParser::Connection *con : source_to_destination) { + // do something + print_verbose("src: " + itos(con->src)); + FBXDocParser::Object *ob = con->DestinationObject(); + mesh = dynamic_cast<FBXDocParser::MeshGeometry *>(ob); + + if (mesh) { + mesh_id = mesh->ID(); + break; + } + } + + // Validate the mesh exists and was retrieved + ERR_CONTINUE_MSG(mesh_id == 0, "mesh id is invalid"); + const std::vector<const FBXDocParser::Cluster *> clusters = skin->Clusters(); + + // NOTE: this will ONLY work on skinned bones (it is by design.) + // A cluster is a skinned bone so SKINS won't contain unskinned bones so we need to pre-add all bones and parent them in a step beforehand. + for (const FBXDocParser::Cluster *cluster : clusters) { + ERR_CONTINUE_MSG(cluster == nullptr, "invalid bone cluster"); + const uint64_t deformer_id = cluster->ID(); + std::vector<const FBXDocParser::Connection *> connections = p_document->GetConnectionsByDestinationSequenced(deformer_id); + + // Weight data always has a node in the scene lets grab the limb's node in the scene :) (reverse set to true since it's the opposite way around) + const FBXDocParser::ModelLimbNode *limb_node = ProcessDOMConnection<FBXDocParser::ModelLimbNode>(p_document, deformer_id, true); + + ERR_CONTINUE_MSG(limb_node == nullptr, "unable to resolve model for skinned bone"); + + const uint64_t model_id = limb_node->ID(); + + // This will never happen, so if it does you know you fucked up. + ERR_CONTINUE_MSG(!state.fbx_bone_map.has(model_id), "missing LimbNode detected"); + + // new bone instance + Ref<FBXBone> bone_element = state.fbx_bone_map[model_id]; + + // + // Bone Weight Information Configuration + // + + // Cache Weight Information into bone for later usage if you want the raw data. + const std::vector<unsigned int> &indexes = cluster->GetIndices(); + const std::vector<float> &weights = cluster->GetWeights(); + Ref<FBXMeshData> mesh_vertex_data; + + // this data will pre-exist if vertex weight information is found + if (state.renderer_mesh_data.has(mesh_id)) { + mesh_vertex_data = state.renderer_mesh_data[mesh_id]; + } else { + mesh_vertex_data.instance(); + state.renderer_mesh_data.insert(mesh_id, mesh_vertex_data); + } + + mesh_vertex_data->armature_id = bone_element->armature_id; + mesh_vertex_data->valid_armature_id = true; + + //print_verbose("storing mesh vertex data for mesh to use later"); + ERR_CONTINUE_MSG(indexes.size() != weights.size(), "[doc] error mismatch between weight info"); + + for (size_t idx = 0; idx < indexes.size(); idx++) { + const size_t vertex_index = indexes[idx]; + const real_t influence_weight = weights[idx]; + + VertexWeightMapping &vm = mesh_vertex_data->vertex_weights[vertex_index]; + vm.weights.push_back(influence_weight); + vm.bones.push_back(0); // bone id is pushed on here during sanitization phase + vm.bones_ref.push_back(bone_element); + } + + for (const int *vertex_index = mesh_vertex_data->vertex_weights.next(nullptr); + vertex_index != nullptr; + vertex_index = mesh_vertex_data->vertex_weights.next(vertex_index)) { + VertexWeightMapping *vm = mesh_vertex_data->vertex_weights.getptr(*vertex_index); + const int influence_count = vm->weights.size(); + if (influence_count > mesh_vertex_data->max_weight_count) { + mesh_vertex_data->max_weight_count = influence_count; + mesh_vertex_data->valid_weight_count = true; + } + } + + if (mesh_vertex_data->max_weight_count > 4) { + if (mesh_vertex_data->max_weight_count > 8) { + ERR_PRINT("[doc] Serious: maximum bone influences is 8 in this branch."); + } + // Clamp to 8 bone vertex influences. + mesh_vertex_data->max_weight_count = 8; + print_verbose("[doc] Using 8 vertex bone influences configuration."); + } else { + mesh_vertex_data->max_weight_count = 4; + print_verbose("[doc] Using 4 vertex bone influences configuration."); + } + } + } + + // do we globally allow for import of materials + // (prevents overwrite of materials; so you can handle them explicitly) + if (state.enable_material_import) { + const std::vector<uint64_t> &materials = p_document->GetMaterialIDs(); + + for (uint64_t material_id : materials) { + FBXDocParser::LazyObject *lazy_material = p_document->GetObject(material_id); + FBXDocParser::Material *mat = (FBXDocParser::Material *)lazy_material->Get<FBXDocParser::Material>(); + ERR_CONTINUE_MSG(!mat, "Could not convert fbx material by id: " + itos(material_id)); + + Ref<FBXMaterial> material; + material.instance(); + material->set_imported_material(mat); + + Ref<StandardMaterial3D> godot_material = material->import_material(state); + + state.cached_materials.insert(material_id, godot_material); + } + } + + // build skin and skeleton information + print_verbose("[doc] Skeleton3D Bone count: " + itos(state.fbx_bone_map.size())); + + // Importing bones using document based method from FBX directly + // We do not use the assimp bone format to determine this information anymore. + if (state.fbx_bone_map.size() > 0) { + // We are using a single skeleton only method here + // this is because we really have no concept of skeletons in FBX + // their are bones in a scene but they have no specific armature + // we can detect armatures but the issue lies in the complexity + // we opted to merge the entire scene onto one skeleton for now + // if we need to change this we have an archive of the old code. + + // bind pose normally only has 1 per mesh but can have more than one + // this is the point of skins + // in FBX first bind pose is the master for the first skin + + // In order to handle the FBX skeleton we must also inverse any parent transforms on the bones + // just to rule out any parent node transforms in the bone data + // this is trivial to do and allows us to use the single skeleton method and merge them + // this means that the nodes from maya kLocators will be preserved as bones + // in the same rig without having to match this across skeletons and merge by detection + // we can just merge and undo any parent transforms + for (Map<uint64_t, Ref<FBXBone>>::Element *bone_element = state.fbx_bone_map.front(); bone_element; bone_element = bone_element->next()) { + Ref<FBXBone> bone = bone_element->value(); + Ref<FBXSkeleton> fbx_skeleton_inst; + + uint64_t armature_id = bone->armature_id; + if (state.skeleton_map.has(armature_id)) { + fbx_skeleton_inst = state.skeleton_map[armature_id]; + } else { + fbx_skeleton_inst.instance(); + state.skeleton_map.insert(armature_id, fbx_skeleton_inst); + } + + print_verbose("populating skeleton with bone: " + bone->bone_name); + + //// populate bone skeleton - since fbx has no DOM for the skeleton just a node. + //bone->bone_skeleton = fbx_skeleton_inst; + + // now populate bone on the armature node list + fbx_skeleton_inst->skeleton_bones.push_back(bone); + + CRASH_COND_MSG(!state.fbx_target_map.has(armature_id), "invalid armature [serious]"); + + Ref<FBXNode> node = state.fbx_target_map[armature_id]; + + CRASH_COND_MSG(node.is_null(), "invalid node [serious]"); + CRASH_COND_MSG(node->pivot_transform.is_null(), "invalid pivot transform [serious]"); + fbx_skeleton_inst->fbx_node = node; + + ERR_CONTINUE_MSG(fbx_skeleton_inst->fbx_node.is_null(), "invalid skeleton node [serious]"); + + // we need to have a valid armature id and the model configured for the bone to be assigned fully. + // happens once per skeleton + + if (state.fbx_target_map.has(armature_id) && !fbx_skeleton_inst->fbx_node->has_model()) { + print_verbose("allocated fbx skeleton primary / armature node for the level: " + fbx_skeleton_inst->fbx_node->node_name); + } else if (!state.fbx_target_map.has(armature_id) && !fbx_skeleton_inst->fbx_node->has_model()) { + print_error("bones are not mapped to an armature node for armature id: " + itos(armature_id) + " bone: " + bone->bone_name); + // this means bone will be removed and not used, which is safe actually and no skeleton will be created. + } + } + + // setup skeleton instances if required :) + for (Map<uint64_t, Ref<FBXSkeleton>>::Element *skeleton_node = state.skeleton_map.front(); skeleton_node; skeleton_node = skeleton_node->next()) { + Ref<FBXSkeleton> &skeleton = skeleton_node->value(); + skeleton->init_skeleton(state); + + ERR_CONTINUE_MSG(skeleton->fbx_node.is_null(), "invalid fbx target map, missing skeleton"); + } + + // This list is not populated + for (Map<uint64_t, Ref<FBXNode>>::Element *skin_mesh = state.MeshNodes.front(); skin_mesh; skin_mesh = skin_mesh->next()) { + } + } + + // build godot node tree + if (state.fbx_node_list.size() > 0) { + for (List<Ref<FBXNode>>::Element *node_element = state.fbx_node_list.front(); + node_element; + node_element = node_element->next()) { + Ref<FBXNode> fbx_node = node_element->get(); + EditorSceneImporterMeshNode3D *mesh_node = nullptr; + Ref<FBXMeshData> mesh_data_precached; + + // check for valid geometry + if (fbx_node->fbx_model == nullptr) { + print_error("[doc] fundamental flaw, submit bug immediately with full import log with verbose logging on"); + } else { + const std::vector<const FBXDocParser::Geometry *> &geometry = fbx_node->fbx_model->GetGeometry(); + for (const FBXDocParser::Geometry *mesh : geometry) { + print_verbose("[doc] [" + itos(mesh->ID()) + "] mesh: " + fbx_node->node_name); + + if (mesh == nullptr) + continue; + + const FBXDocParser::MeshGeometry *mesh_geometry = dynamic_cast<const FBXDocParser::MeshGeometry *>(mesh); + if (mesh_geometry) { + uint64_t mesh_id = mesh_geometry->ID(); + + // this data will pre-exist if vertex weight information is found + if (state.renderer_mesh_data.has(mesh_id)) { + mesh_data_precached = state.renderer_mesh_data[mesh_id]; + } else { + mesh_data_precached.instance(); + state.renderer_mesh_data.insert(mesh_id, mesh_data_precached); + } + + mesh_data_precached->mesh_node = fbx_node; + + // mesh node, mesh id + mesh_node = mesh_data_precached->create_fbx_mesh(state, mesh_geometry, fbx_node->fbx_model, (p_flags & IMPORT_USE_COMPRESSION) != 0); + if (!state.MeshNodes.has(mesh_id)) { + state.MeshNodes.insert(mesh_id, fbx_node); + } + } + + const FBXDocParser::ShapeGeometry *shape_geometry = dynamic_cast<const FBXDocParser::ShapeGeometry *>(mesh); + if (shape_geometry != nullptr) { + print_verbose("[doc] valid shape geometry converted"); + } + } + } + + Ref<FBXSkeleton> node_skeleton = fbx_node->skeleton_node; + + if (node_skeleton.is_valid()) { + Skeleton3D *skel = node_skeleton->skeleton; + fbx_node->godot_node = skel; + } else if (mesh_node == nullptr) { + fbx_node->godot_node = memnew(Node3D); + } else { + fbx_node->godot_node = mesh_node; + } + + fbx_node->godot_node->set_name(fbx_node->node_name); + + // assign parent if valid + if (fbx_node->fbx_parent.is_valid()) { + fbx_node->fbx_parent->godot_node->add_child(fbx_node->godot_node); + fbx_node->godot_node->set_owner(state.root_owner); + } + + // Node Transform debug, set local xform data. + fbx_node->godot_node->set_transform(get_unscaled_transform(fbx_node->pivot_transform->LocalTransform, state.scale)); + + // populate our mesh node reference + if (mesh_node != nullptr && mesh_data_precached.is_valid()) { + mesh_data_precached->godot_mesh_instance = mesh_node; + } + } + } + + for (Map<uint64_t, Ref<FBXMeshData>>::Element *mesh_data = state.renderer_mesh_data.front(); mesh_data; mesh_data = mesh_data->next()) { + const uint64_t mesh_id = mesh_data->key(); + Ref<FBXMeshData> mesh = mesh_data->value(); + + const FBXDocParser::MeshGeometry *mesh_geometry = p_document->GetObject(mesh_id)->Get<FBXDocParser::MeshGeometry>(); + + ERR_CONTINUE_MSG(mesh->mesh_node.is_null(), "invalid mesh allocation"); + + const FBXDocParser::Skin *mesh_skin = mesh_geometry->DeformerSkin(); + + if (!mesh_skin) { + continue; // safe to continue + } + + // + // Skin bone configuration + // + + // + // Get Mesh Node Xform only + // + //ERR_CONTINUE_MSG(!state.fbx_target_map.has(mesh_id), "invalid xform for the skin pose: " + itos(mesh_id)); + //Ref<FBXNode> mesh_node_xform_data = state.fbx_target_map[mesh_id]; + + if (!mesh_skin) { + continue; // not a deformer. + } + + if (mesh_skin->Clusters().size() == 0) { + continue; // possibly buggy mesh + } + + // Lookup skin or create it if it's not found. + Ref<Skin> skin; + if (!state.MeshSkins.has(mesh_id)) { + print_verbose("Created new skin"); + skin.instance(); + state.MeshSkins.insert(mesh_id, skin); + } else { + print_verbose("Grabbed skin"); + skin = state.MeshSkins[mesh_id]; + } + + for (const FBXDocParser::Cluster *cluster : mesh_skin->Clusters()) { + // node or bone this cluster targets (in theory will only be a bone target) + uint64_t skin_target_id = cluster->TargetNode()->ID(); + + print_verbose("adding cluster [" + itos(cluster->ID()) + "] " + String(cluster->Name().c_str()) + " for target: [" + itos(skin_target_id) + "] " + String(cluster->TargetNode()->Name().c_str())); + ERR_CONTINUE_MSG(!state.fbx_bone_map.has(skin_target_id), "no bone found by that ID? locator"); + + const Ref<FBXBone> bone = state.fbx_bone_map[skin_target_id]; + const Ref<FBXSkeleton> skeleton = bone->fbx_skeleton; + const Ref<FBXNode> skeleton_node = skeleton->fbx_node; + + skin->add_named_bind( + bone->bone_name, + get_unscaled_transform( + skeleton_node->pivot_transform->GlobalTransform.affine_inverse() * cluster->TransformLink().affine_inverse(), state.scale)); + } + + print_verbose("cluster name / id: " + String(mesh_skin->Name().c_str()) + " [" + itos(mesh_skin->ID()) + "]"); + print_verbose("skeleton has " + itos(state.fbx_bone_map.size()) + " binds"); + print_verbose("fbx skin has " + itos(mesh_skin->Clusters().size()) + " binds"); + } + + // mesh data iteration for populating skeleton mapping + for (Map<uint64_t, Ref<FBXMeshData>>::Element *mesh_data = state.renderer_mesh_data.front(); mesh_data; mesh_data = mesh_data->next()) { + Ref<FBXMeshData> mesh = mesh_data->value(); + const uint64_t mesh_id = mesh_data->key(); + EditorSceneImporterMeshNode3D *mesh_instance = mesh->godot_mesh_instance; + const int mesh_weights = mesh->max_weight_count; + Ref<FBXSkeleton> skeleton; + const bool valid_armature = mesh->valid_armature_id; + const uint64_t armature = mesh->armature_id; + + if (mesh_weights > 0) { + // this is a bug, it means the weights were found but the skeleton wasn't + ERR_CONTINUE_MSG(!valid_armature, "[doc] fbx armature is missing"); + } else { + continue; // safe to continue not a bug just a normal mesh + } + + if (state.skeleton_map.has(armature)) { + skeleton = state.skeleton_map[armature]; + print_verbose("[doc] armature mesh to skeleton mapping has been allocated"); + } else { + print_error("[doc] unable to find armature mapping"); + } + + ERR_CONTINUE_MSG(!mesh_instance, "[doc] invalid mesh mapping for skeleton assignment"); + ERR_CONTINUE_MSG(skeleton.is_null(), "[doc] unable to resolve the correct skeleton but we have weights!"); + + mesh_instance->set_skeleton_path(mesh_instance->get_path_to(skeleton->skeleton)); + print_verbose("[doc] allocated skeleton to mesh " + mesh_instance->get_name()); + + // do we have a mesh skin for this mesh + ERR_CONTINUE_MSG(!state.MeshSkins.has(mesh_id), "no skin found for mesh"); + + Ref<Skin> mesh_skin = state.MeshSkins[mesh_id]; + + ERR_CONTINUE_MSG(mesh_skin.is_null(), "invalid skin stored in map"); + print_verbose("[doc] allocated skin to mesh " + mesh_instance->get_name()); + mesh_instance->set_skin(mesh_skin); + } + + // build skin and skeleton information + print_verbose("[doc] Skeleton3D Bone count: " + itos(state.fbx_bone_map.size())); + const FBXDocParser::FileGlobalSettings *FBXSettings = p_document->GlobalSettingsPtr(); + + // Configure constraints + // NOTE: constraints won't be added quite yet, we don't have a real need for them *yet*. (they can be supported later on) + // const std::vector<uint64_t> fbx_constraints = p_document->GetConstraintStackIDs(); + + // get the animation FPS + float fps_setting = ImportUtils::get_fbx_fps(FBXSettings); + + // enable animation import, only if local animation is enabled + if (state.enable_animation_import && (p_flags & IMPORT_ANIMATION)) { + // document animation stack list - get by ID so we can unload any non used animation stack + const std::vector<uint64_t> animation_stack = p_document->GetAnimationStackIDs(); + + for (uint64_t anim_id : animation_stack) { + FBXDocParser::LazyObject *lazyObject = p_document->GetObject(anim_id); + const FBXDocParser::AnimationStack *stack = lazyObject->Get<FBXDocParser::AnimationStack>(); + + if (stack != nullptr) { + String animation_name = ImportUtils::FBXNodeToName(stack->Name()); + print_verbose("Valid animation stack has been found: " + animation_name); + // ReferenceTime is the same for some animations? + // LocalStop time is the start and end time + float r_start = CONVERT_FBX_TIME(stack->ReferenceStart()); + float r_stop = CONVERT_FBX_TIME(stack->ReferenceStop()); + float start_time = CONVERT_FBX_TIME(stack->LocalStart()); + float end_time = CONVERT_FBX_TIME(stack->LocalStop()); + float duration = end_time - start_time; + + print_verbose("r_start " + rtos(r_start) + ", r_stop " + rtos(r_stop)); + print_verbose("start_time" + rtos(start_time) + " end_time " + rtos(end_time)); + print_verbose("anim duration : " + rtos(duration)); + + // we can safely create the animation player + if (state.animation_player == nullptr) { + print_verbose("Creating animation player"); + state.animation_player = memnew(AnimationPlayer); + state.root->add_child(state.animation_player); + state.animation_player->set_owner(state.root_owner); + } + + Ref<Animation> animation; + animation.instance(); + animation->set_name(animation_name); + animation->set_length(duration); + + print_verbose("Animation length: " + rtos(animation->get_length()) + " seconds"); + + // i think assimp was duplicating things, this lets me know to just reference or ignore this to prevent duplicate information in tracks + // this would mean that we would be doing three times as much work per track if my theory is correct. + // this was not the case but this is a good sanity check for the animation handler from the document. + // it also lets us know if the FBX specification massively changes the animation system, in theory such a change would make this show + // an fbx specification error, so best keep it in + // the overhead is tiny. + Map<uint64_t, const FBXDocParser::AnimationCurve *> CheckForDuplication; + + const std::vector<const FBXDocParser::AnimationLayer *> &layers = stack->Layers(); + print_verbose("FBX Animation layers: " + itos(layers.size())); + for (const FBXDocParser::AnimationLayer *layer : layers) { + std::vector<const FBXDocParser::AnimationCurveNode *> node_list = layer->Nodes(); + print_verbose("Layer: " + ImportUtils::FBXNodeToName(layer->Name()) + ", " + " AnimCurveNode count " + itos(node_list.size())); + + // first thing to do here is that i need to first get the animcurvenode to a Vector3 + // we now need to put this into the track information for godot. + // to do this we need to know which track is what? + + // target id, [ track name, [time index, vector] ] + // new map needs to be [ track name, keyframe_data ] + Map<uint64_t, Map<StringName, FBXTrack>> AnimCurveNodes; + + // struct AnimTrack { + // // Animation track can be + // // visible, T, R, S + // Map<StringName, Map<uint64_t, Vector3> > animation_track; + // }; + + // Map<uint64_t, AnimTrack> AnimCurveNodes; + + // so really, what does this mean to make an animtion track. + // we need to know what object the curves are for. + // we need the target ID and the target name for the track reduction. + + FBXDocParser::Model::RotOrder quat_rotation_order = FBXDocParser::Model::RotOrder_EulerXYZ; + + // T:: R:: S:: Visible:: Custom:: + for (const FBXDocParser::AnimationCurveNode *curve_node : node_list) { + // when Curves() is called the curves are actually read, we could replace this with our own ProcessDomConnection code here if required. + // We may need to do this but ideally we use Curves + // note: when you call this there might be a delay in opening it + // uses mutable type to 'cache' the response until the AnimationCurveNode is cleaned up. + std::map<std::string, const FBXDocParser::AnimationCurve *> curves = curve_node->Curves(); + const FBXDocParser::Object *object = curve_node->Target(); + const FBXDocParser::Model *target = curve_node->TargetAsModel(); + if (target == nullptr) { + if (object != nullptr) { + print_error("[doc] warning failed to find a target Model for curve: " + String(object->Name().c_str())); + } else { + //print_error("[doc] failed to resolve object"); + continue; + } + + continue; + } else { + //print_verbose("[doc] applied rotation order: " + itos(target->RotationOrder())); + quat_rotation_order = target->RotationOrder(); + } + + uint64_t target_id = target->ID(); + String target_name = ImportUtils::FBXNodeToName(target->Name()); + + const FBXDocParser::PropertyTable *properties = curve_node->Props(); + bool got_x = false, got_y = false, got_z = false; + float offset_x = FBXDocParser::PropertyGet<float>(properties, "d|X", got_x); + float offset_y = FBXDocParser::PropertyGet<float>(properties, "d|Y", got_y); + float offset_z = FBXDocParser::PropertyGet<float>(properties, "d|Z", got_z); + + String curve_node_name = ImportUtils::FBXNodeToName(curve_node->Name()); + + // Reduce all curves for this node into a single container + // T, R, S is what we expect, although other tracks are possible + // like for example visibility tracks. + + // We are not ordered here, we don't care about ordering, this happens automagically by godot when we insert with the + // key time :), so order is unimportant because the insertion will happen at a time index + // good to know: we do not need a list of these in another format :) + //Map<String, Vector<const Assimp::FBX::AnimationCurve *> > unordered_track; + + // T + // R + // S + // Map[String, List<VECTOR>] + + // So this is a reduction of the animation curve nodes + // We build this as a lookup, this is essentially our 'animation track' + //AnimCurveNodes.insert(curve_node_name, Map<uint64_t, Vector3>()); + + // create the animation curve information with the target id + // so the point of this makes a track with the name "T" for example + // the target ID is also set here, this means we don't need to do anything extra when we are in the 'create all animation tracks' step + FBXTrack &keyframe_map = AnimCurveNodes[target_id][StringName(curve_node_name)]; + + if (got_x && got_y && got_z) { + Vector3 default_value = Vector3(offset_x, offset_y, offset_z); + keyframe_map.default_value = default_value; + keyframe_map.has_default = true; + //print_verbose("track name: " + curve_node_name); + //print_verbose("xyz default: " + default_value); + } + // target id, [ track name, [time index, vector] ] + // Map<uint64_t, Map<StringName, Map<uint64_t, Vector3> > > AnimCurveNodes; + + // we probably need the target id here. + // so map[uint64_t map]... + // Map<uint64_t, Vector3D> translation_keys, rotation_keys, scale_keys; + + // extra const required by C++11 colon/Range operator + // note: do not use C++17 syntax here for dicts. + // this is banned in Godot. + for (std::pair<const std::string, const FBXDocParser::AnimationCurve *> &kvp : curves) { + const String curve_element = ImportUtils::FBXNodeToName(kvp.first); + const FBXDocParser::AnimationCurve *curve = kvp.second; + String curve_name = ImportUtils::FBXNodeToName(curve->Name()); + uint64_t curve_id = curve->ID(); + + if (CheckForDuplication.has(curve_id)) { + print_error("(FBX spec changed?) We found a duplicate curve being used for an alternative node - report to godot issue tracker"); + } else { + CheckForDuplication.insert(curve_id, curve); + } + + // FBX has no name for AnimCurveNode::, most of the time, not seen any with valid name here. + const std::map<int64_t, float> &track_time = curve->GetValueTimeTrack(); + + if (track_time.size() > 0) { + for (std::pair<int64_t, float> keyframe : track_time) { + if (curve_element == "d|X") { + keyframe_map.keyframes[keyframe.first].x = keyframe.second; + } else if (curve_element == "d|Y") { + keyframe_map.keyframes[keyframe.first].y = keyframe.second; + } else if (curve_element == "d|Z") { + keyframe_map.keyframes[keyframe.first].z = keyframe.second; + } else { + //print_error("FBX Unsupported element: " + curve_element); + } + + //print_verbose("[" + itos(target_id) + "] Keyframe added: " + itos(keyframe_map.size())); + + //print_verbose("Keyframe t:" + rtos(animation_track_time) + " v: " + rtos(keyframe.second)); + } + } + } + } + + // Map<uint64_t, Map<StringName, Map<uint64_t, Vector3> > > AnimCurveNodes; + // add this animation track here + + // target id, [ track name, [time index, vector] ] + //std::map<uint64_t, std::map<StringName, FBXTrack > > AnimCurveNodes; + for (Map<uint64_t, Map<StringName, FBXTrack>>::Element *track = AnimCurveNodes.front(); track; track = track->next()) { + // 5 tracks + // current track index + // track count is 5 + // track count is 5. + // next track id is 5. + const uint64_t target_id = track->key(); + int track_idx = animation->add_track(Animation::TYPE_TRANSFORM); + + // animation->track_set_path(track_idx, node_path); + // animation->track_set_path(track_idx, node_path); + Ref<FBXBone> bone; + + // note we must not run the below code if the entry doesn't exist, it will create dummy entries which is very bad. + // remember that state.fbx_bone_map[target_id] will create a new entry EVEN if you only read. + // this would break node animation targets, so if you change this be warned. :) + if (state.fbx_bone_map.has(target_id)) { + bone = state.fbx_bone_map[target_id]; + } + + Transform target_transform; + + if (state.fbx_target_map.has(target_id)) { + Ref<FBXNode> node_ref = state.fbx_target_map[target_id]; + target_transform = node_ref->pivot_transform->GlobalTransform; + //print_verbose("[doc] allocated animation node transform"); + } + + //int size_targets = state.fbx_target_map.size(); + //print_verbose("Target ID map: " + itos(size_targets)); + //print_verbose("[doc] debug bone map size: " + itos(state.fbx_bone_map.size())); + + // if this is a skeleton mapped track we can just set the path for the track. + // todo: implement node paths here at some + if (state.fbx_bone_map.size() > 0 && state.fbx_bone_map.has(target_id)) { + if (bone->fbx_skeleton.is_valid() && bone.is_valid()) { + Ref<FBXSkeleton> fbx_skeleton = bone->fbx_skeleton; + String bone_path = state.root->get_path_to(fbx_skeleton->skeleton); + bone_path += ":" + fbx_skeleton->skeleton->get_bone_name(bone->godot_bone_id); + print_verbose("[doc] track bone path: " + bone_path); + NodePath path = bone_path; + animation->track_set_path(track_idx, path); + } + } else if (state.fbx_target_map.has(target_id)) { + //print_verbose("[doc] we have a valid target for a node animation"); + Ref<FBXNode> target_node = state.fbx_target_map[target_id]; + if (target_node.is_valid() && target_node->godot_node != nullptr) { + String node_path = state.root->get_path_to(target_node->godot_node); + NodePath path = node_path; + animation->track_set_path(track_idx, path); + //print_verbose("[doc] node animation path: " + node_path); + } + } else { + // note: this could actually be unsafe this means we should be careful about continuing here, if we see bizzare effects later we should disable this. + // I am not sure if this is unsafe or not, testing will tell us this. + print_error("[doc] invalid fbx target detected for this track"); + continue; + } + + // everything in FBX and Maya is a node therefore if this happens something is seriously broken. + if (!state.fbx_target_map.has(target_id)) { + print_error("unable to resolve this to an FBX object."); + continue; + } + + Ref<FBXNode> target_node = state.fbx_target_map[target_id]; + const FBXDocParser::Model *model = target_node->fbx_model; + const FBXDocParser::PropertyTable *props = model->Props(); + + Map<StringName, FBXTrack> &track_data = track->value(); + FBXTrack &translation_keys = track_data[StringName("T")]; + FBXTrack &rotation_keys = track_data[StringName("R")]; + FBXTrack &scale_keys = track_data[StringName("S")]; + + double increment = 1.0f / fps_setting; + double time = 0.0f; + + bool last = false; + + Vector<Vector3> pos_values; + Vector<float> pos_times; + Vector<Vector3> scale_values; + Vector<float> scale_times; + Vector<Quat> rot_values; + Vector<float> rot_times; + + double max_duration = 0; + double anim_length = animation->get_length(); + + for (std::pair<int64_t, Vector3> position_key : translation_keys.keyframes) { + pos_values.push_back(position_key.second * state.scale); + double animation_track_time = CONVERT_FBX_TIME(position_key.first); + + if (animation_track_time > max_duration) { + max_duration = animation_track_time; + } + + //print_verbose("pos keyframe: t:" + rtos(animation_track_time) + " value " + position_key.second); + pos_times.push_back(animation_track_time); + } + + for (std::pair<int64_t, Vector3> scale_key : scale_keys.keyframes) { + scale_values.push_back(scale_key.second); + double animation_track_time = CONVERT_FBX_TIME(scale_key.first); + + if (animation_track_time > max_duration) { + max_duration = animation_track_time; + } + //print_verbose("scale keyframe t:" + rtos(animation_track_time)); + scale_times.push_back(animation_track_time); + } + + // + // Pre and Post keyframe rotation handler + // -- Required because Maya and Autodesk <3 the pain when it comes to implementing animation code! enjoy <3 + + bool got_pre = false; + bool got_post = false; + + Quat post_rotation; + Quat pre_rotation; + + // Rotation matrix + const Vector3 &PreRotation = FBXDocParser::PropertyGet<Vector3>(props, "PreRotation", got_pre); + const Vector3 &PostRotation = FBXDocParser::PropertyGet<Vector3>(props, "PostRotation", got_post); + + FBXDocParser::Model::RotOrder rot_order = model->RotationOrder(); + if (got_pre) { + pre_rotation = ImportUtils::EulerToQuaternion(rot_order, ImportUtils::deg2rad(PreRotation)); + } + if (got_post) { + post_rotation = ImportUtils::EulerToQuaternion(rot_order, ImportUtils::deg2rad(PostRotation)); + } + + Quat lastQuat = Quat(); + + for (std::pair<int64_t, Vector3> rotation_key : rotation_keys.keyframes) { + double animation_track_time = CONVERT_FBX_TIME(rotation_key.first); + + //print_verbose("euler rotation key: " + rotation_key.second); + Quat rot_key_value = ImportUtils::EulerToQuaternion(quat_rotation_order, ImportUtils::deg2rad(rotation_key.second)); + + if (lastQuat != Quat() && rot_key_value.dot(lastQuat) < 0) { + rot_key_value.x = -rot_key_value.x; + rot_key_value.y = -rot_key_value.y; + rot_key_value.z = -rot_key_value.z; + rot_key_value.w = -rot_key_value.w; + } + // pre_post rotation possibly could fix orientation + Quat final_rotation = pre_rotation * rot_key_value * post_rotation; + + lastQuat = final_rotation; + + if (animation_track_time > max_duration) { + max_duration = animation_track_time; + } + + rot_values.push_back(final_rotation); + rot_times.push_back(animation_track_time); + } + + bool valid_rest = false; + Transform bone_rest; + int skeleton_bone = -1; + if (state.fbx_bone_map.has(target_id)) { + if (bone.is_valid() && bone->fbx_skeleton.is_valid()) { + skeleton_bone = bone->godot_bone_id; + if (skeleton_bone >= 0) { + bone_rest = bone->fbx_skeleton->skeleton->get_bone_rest(skeleton_bone); + valid_rest = true; + } + } + + if (!valid_rest) { + print_verbose("invalid rest!"); + } + } + + const Vector3 def_pos = translation_keys.has_default ? (translation_keys.default_value * state.scale) : bone_rest.origin; + const Quat def_rot = rotation_keys.has_default ? ImportUtils::EulerToQuaternion(quat_rotation_order, ImportUtils::deg2rad(rotation_keys.default_value)) : bone_rest.basis.get_rotation_quat(); + const Vector3 def_scale = scale_keys.has_default ? scale_keys.default_value : bone_rest.basis.get_scale(); + print_verbose("track defaults: p(" + def_pos + ") s(" + def_scale + ") r(" + def_rot + ")"); + + while (true) { + Vector3 pos = def_pos; + Quat rot = def_rot; + Vector3 scale = def_scale; + + if (pos_values.size()) { + pos = _interpolate_track<Vector3>(pos_times, pos_values, time, + AssetImportAnimation::INTERP_LINEAR); + } + + if (rot_values.size()) { + rot = _interpolate_track<Quat>(rot_times, rot_values, time, + AssetImportAnimation::INTERP_LINEAR); + } + + if (scale_values.size()) { + scale = _interpolate_track<Vector3>(scale_times, scale_values, time, + AssetImportAnimation::INTERP_LINEAR); + } + + // node animations must also include pivots + if (skeleton_bone >= 0) { + Transform xform = Transform(); + xform.basis.set_quat_scale(rot, scale); + xform.origin = pos; + const Transform t = bone_rest.affine_inverse() * xform; + + // populate this again + rot = t.basis.get_rotation_quat(); + rot.normalize(); + scale = t.basis.get_scale(); + pos = t.origin; + } + + animation->transform_track_insert_key(track_idx, time, pos, rot, scale); + + if (last) { + break; + } + + time += increment; + if (time > anim_length) { + last = true; + time = anim_length; + break; + } + } + } + } + state.animation_player->add_animation(animation_name, animation); + } + } + + // AnimStack elements contain start stop time and name of animation + // AnimLayer is the current active layer of the animation (multiple layers can be active we only support 1) + // AnimCurveNode has a OP link back to the model which is the real node. + // AnimCurveNode has a direct link to AnimationCurve (of which it may have more than one) + + // Store animation stack in list + // iterate over all AnimStacks like the cache node algorithm recursively + // this can then be used with ProcessDomConnection<> to link from + // AnimStack:: <-- (OO) --> AnimLayer:: <-- (OO) --> AnimCurveNode:: (which can OP resolve) to Model:: + } + + // + // Cleanup operations - explicit to prevent errors on shutdown - found that ref to ref does behave badly sometimes. + // + + state.renderer_mesh_data.clear(); + state.MeshSkins.clear(); + state.fbx_target_map.clear(); + state.fbx_node_list.clear(); + + for (Map<uint64_t, Ref<FBXBone>>::Element *element = state.fbx_bone_map.front(); element; element = element->next()) { + Ref<FBXBone> bone = element->value(); + bone->parent_bone.unref(); + bone->node.unref(); + bone->fbx_skeleton.unref(); + } + + for (Map<uint64_t, Ref<FBXSkeleton>>::Element *element = state.skeleton_map.front(); element; element = element->next()) { + Ref<FBXSkeleton> skel = element->value(); + skel->fbx_node.unref(); + skel->skeleton_bones.clear(); + } + + state.fbx_bone_map.clear(); + state.skeleton_map.clear(); + state.fbx_root_node.unref(); + + return scene_root; +} + +void EditorSceneImporterFBX::BuildDocumentBones(Ref<FBXBone> p_parent_bone, + ImportState &state, const FBXDocParser::Document *p_doc, + uint64_t p_id) { + const std::vector<const FBXDocParser::Connection *> &conns = p_doc->GetConnectionsByDestinationSequenced(p_id, "Model"); + // FBX can do an join like this + // Model -> SubDeformer (bone) -> Deformer (skin pose) + // This is important because we need to somehow link skin back to bone id in skeleton :) + // The rules are: + // A subdeformer will exist if 'limbnode' class tag present + // The subdeformer will not necessarily have a deformer as joints do not have one + for (const FBXDocParser::Connection *con : conns) { + // goto: bone creation + //print_verbose("con: " + String(con->PropertyName().c_str())); + + // ignore object-property links we want the object to object links nothing else + if (con->PropertyName().length()) { + continue; + } + + // convert connection source object into Object base class + const FBXDocParser::Object *const object = con->SourceObject(); + + if (nullptr == object) { + print_verbose("failed to convert source object for Model link"); + continue; + } + + // FBX Model::Cube, Model::Bone001, etc elements + // This detects if we can cast the object into this model structure. + const FBXDocParser::Model *const model = dynamic_cast<const FBXDocParser::Model *>(object); + + // declare our bone element reference (invalid, unless we create a bone in this step) + // this lets us pass valid armature information into children objects and this is why we moved this up here + // previously this was created .instanced() on the same line. + Ref<FBXBone> bone_element; + + if (model != nullptr) { + // model marked with limb node / casted. + const FBXDocParser::ModelLimbNode *const limb_node = dynamic_cast<const FBXDocParser::ModelLimbNode *>(model); + if (limb_node != nullptr) { + // Write bone into bone list for FBX + + ERR_FAIL_COND_MSG(state.fbx_bone_map.has(limb_node->ID()), "[serious] duplicate LimbNode detected"); + + bool parent_is_bone = state.fbx_bone_map.find(p_id); + bone_element.instance(); + + // used to build the bone hierarchy in the skeleton + bone_element->parent_bone_id = parent_is_bone ? p_id : 0; + bone_element->valid_parent = parent_is_bone; + bone_element->limb_node = limb_node; + + // parent is a node and this is the first bone + if (!parent_is_bone) { + uint64_t armature_id = p_id; + bone_element->valid_armature_id = true; + bone_element->armature_id = armature_id; + print_verbose("[doc] valid armature has been configured for first child: " + itos(armature_id)); + } else if (p_parent_bone.is_valid()) { + if (p_parent_bone->valid_armature_id) { + bone_element->valid_armature_id = true; + bone_element->armature_id = p_parent_bone->armature_id; + print_verbose("[doc] bone has valid armature id:" + itos(bone_element->armature_id)); + } else { + print_error("[doc] unassigned armature id: " + String(limb_node->Name().c_str())); + } + } else { + print_error("[doc] error is this a bone? " + String(limb_node->Name().c_str())); + } + + if (!parent_is_bone) { + print_verbose("[doc] Root bone: " + bone_element->bone_name); + } + + uint64_t limb_id = limb_node->ID(); + bone_element->bone_id = limb_id; + bone_element->bone_name = ImportUtils::FBXNodeToName(model->Name()); + bone_element->parent_bone = p_parent_bone; + + // insert limb by ID into list. + state.fbx_bone_map.insert(limb_node->ID(), bone_element); + } + + // recursion call - child nodes + BuildDocumentBones(bone_element, state, p_doc, model->ID()); + } + } +} + +void EditorSceneImporterFBX::BuildDocumentNodes( + Ref<PivotTransform> parent_transform, + ImportState &state, + const FBXDocParser::Document *p_doc, + uint64_t id, + Ref<FBXNode> parent_node) { + // tree + // here we get the node 0 on the root by default + const std::vector<const FBXDocParser::Connection *> &conns = p_doc->GetConnectionsByDestinationSequenced(id, "Model"); + + // branch + for (const FBXDocParser::Connection *con : conns) { + // ignore object-property links + if (con->PropertyName().length()) { + // really important we document why this is ignored. + print_verbose("ignoring property link - no docs on why this is ignored"); + continue; + } + + // convert connection source object into Object base class + // Source objects can exist with 'null connections' this means that we only for sure know the source exists. + const FBXDocParser::Object *const source_object = con->SourceObject(); + + if (nullptr == source_object) { + print_verbose("failed to convert source object for Model link"); + continue; + } + + // FBX Model::Cube, Model::Bone001, etc elements + // This detects if we can cast the object into this model structure. + const FBXDocParser::Model *const model = dynamic_cast<const FBXDocParser::Model *>(source_object); + // model is the current node + if (nullptr != model) { + uint64_t current_node_id = model->ID(); + + Ref<FBXNode> new_node; + new_node.instance(); + new_node->current_node_id = current_node_id; + new_node->node_name = ImportUtils::FBXNodeToName(model->Name()); + + Ref<PivotTransform> fbx_transform; + fbx_transform.instance(); + fbx_transform->set_parent(parent_transform); + fbx_transform->set_model(model); + fbx_transform->debug_pivot_xform("name: " + new_node->node_name); + fbx_transform->Execute(); + + new_node->set_pivot_transform(fbx_transform); + + // check if this node is a bone + if (state.fbx_bone_map.has(current_node_id)) { + Ref<FBXBone> bone = state.fbx_bone_map[current_node_id]; + if (bone.is_valid()) { + bone->set_node(new_node); + print_verbose("allocated bone data: " + bone->bone_name); + } + } + + // set the model, we can't just assign this safely + new_node->set_model(model); + + if (parent_node.is_valid()) { + new_node->set_parent(parent_node); + } else { + new_node->set_parent(state.fbx_root_node); + } + + CRASH_COND_MSG(new_node->pivot_transform.is_null(), "invalid fbx target map pivot transform [serious]"); + + // populate lookup tables with references + // [fbx_node_id, fbx_node] + + state.fbx_node_list.push_back(new_node); + if (!state.fbx_target_map.has(new_node->current_node_id)) { + state.fbx_target_map[new_node->current_node_id] = new_node; + } + + // print node name + print_verbose("[doc] new node " + new_node->node_name); + + // sub branches + BuildDocumentNodes(new_node->pivot_transform, state, p_doc, current_node_id, new_node); + } + } +} diff --git a/modules/fbx/editor_scene_importer_fbx.h b/modules/fbx/editor_scene_importer_fbx.h new file mode 100644 index 0000000000..39f8648b0f --- /dev/null +++ b/modules/fbx/editor_scene_importer_fbx.h @@ -0,0 +1,135 @@ +/*************************************************************************/ +/* editor_scene_importer_fbx.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef EDITOR_SCENE_IMPORTER_FBX_H +#define EDITOR_SCENE_IMPORTER_FBX_H + +#ifdef TOOLS_ENABLED + +#include "data/import_state.h" +#include "tools/import_utils.h" + +#include "core/core_bind.h" +#include "core/io/resource_importer.h" +#include "core/string/ustring.h" +#include "core/templates/local_vector.h" +#include "core/templates/vector.h" +#include "core/variant/dictionary.h" +#include "editor/import/resource_importer_scene.h" +#include "editor/project_settings_editor.h" +#include "scene/3d/mesh_instance_3d.h" +#include "scene/3d/node_3d.h" +#include "scene/3d/skeleton_3d.h" +#include "scene/animation/animation_player.h" +#include "scene/resources/animation.h" +#include "scene/resources/surface_tool.h" + +#include "fbx_parser/FBXDocument.h" +#include "fbx_parser/FBXImportSettings.h" +#include "fbx_parser/FBXMeshGeometry.h" +#include "fbx_parser/FBXUtil.h" + +#define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000LL + +class EditorSceneImporterFBX : public EditorSceneImporter { +private: + GDCLASS(EditorSceneImporterFBX, EditorSceneImporter); + + struct AssetImportAnimation { + enum Interpolation { + INTERP_LINEAR, + INTERP_STEP, + INTERP_CATMULLROMSPLINE, + INTERP_CUBIC_SPLINE + }; + }; + + // ------------------------------------------------------------------------------------------------ + template <typename T> + const T *ProcessDOMConnection( + const FBXDocParser::Document *doc, + uint64_t current_element, + bool reverse_lookup = false) { + const std::vector<const FBXDocParser::Connection *> &conns = reverse_lookup ? doc->GetConnectionsByDestinationSequenced(current_element) : doc->GetConnectionsBySourceSequenced(current_element); + //print_verbose("[doc] looking for " + String(element_to_find)); + // using the temp pattern here so we can debug before it returns + // in some cases we return too early, with 'deformer object base class' in wrong place + // in assimp this means we can accidentally return too early... + const T *return_obj = nullptr; + + for (const FBXDocParser::Connection *con : conns) { + const FBXDocParser::Object *source_object = con->SourceObject(); + const FBXDocParser::Object *dest_object = con->DestinationObject(); + if (source_object && dest_object != nullptr) { + //print_verbose("[doc] connection name: " + String(source_object->Name().c_str()) + ", dest: " + String(dest_object->Name().c_str())); + const T *temp = dynamic_cast<const T *>(reverse_lookup ? source_object : dest_object); + if (temp) { + return_obj = temp; + } + } + } + + if (return_obj != nullptr) { + //print_verbose("[doc] returned valid element"); + //print_verbose("Found object for bone"); + return return_obj; + } + + // safe to return nothing, need to use nullptr here as nullptr is used internally for FBX document. + return nullptr; + } + + void BuildDocumentBones(Ref<FBXBone> p_parent_bone, + ImportState &state, const FBXDocParser::Document *p_doc, + uint64_t p_id); + + void BuildDocumentNodes(Ref<PivotTransform> parent_transform, ImportState &state, const FBXDocParser::Document *doc, uint64_t id, Ref<FBXNode> fbx_parent); + + Node3D *_generate_scene(const String &p_path, const FBXDocParser::Document *p_document, + const uint32_t p_flags, + int p_bake_fps, + const int32_t p_max_bone_weights, + bool p_is_blender_fbx); + + template <class T> + T _interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time, AssetImportAnimation::Interpolation p_interp); + void _register_project_setting_import(const String generic, const String import_setting_string, const Vector<String> &exts, List<String> *r_extensions, const bool p_enabled) const; + +public: + EditorSceneImporterFBX() {} + ~EditorSceneImporterFBX() {} + + virtual void get_extensions(List<String> *r_extensions) const override; + virtual uint32_t get_import_flags() const override; + virtual Node3D *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = NULL) override; +}; + +#endif // TOOLS_ENABLED +#endif // EDITOR_SCENE_IMPORTER_FBX_H diff --git a/modules/fbx/fbx_parser/ByteSwapper.h b/modules/fbx/fbx_parser/ByteSwapper.h new file mode 100644 index 0000000000..f759c9117c --- /dev/null +++ b/modules/fbx/fbx_parser/ByteSwapper.h @@ -0,0 +1,282 @@ +/*************************************************************************/ +/* ByteSwapper.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2020, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Helper class tp perform various byte oder swappings + (e.g. little to big endian) */ +#ifndef BYTE_SWAPPER_H +#define BYTE_SWAPPER_H + +#include <stdint.h> +#include <algorithm> +#include <locale> + +namespace FBXDocParser { +// -------------------------------------------------------------------------------------- +/** Defines some useful byte order swap routines. + * + * This is required to read big-endian model formats on little-endian machines, + * and vice versa. Direct use of this class is DEPRECATED. Use #StreamReader instead. */ +// -------------------------------------------------------------------------------------- +class ByteSwap { + ByteSwap() {} + +public: + // ---------------------------------------------------------------------- + /** Swap two bytes of data + * @param[inout] _szOut A void* to save the reintcasts for the caller. */ + static inline void Swap2(void *_szOut) { + uint8_t *const szOut = reinterpret_cast<uint8_t *>(_szOut); + std::swap(szOut[0], szOut[1]); + } + + // ---------------------------------------------------------------------- + /** Swap four bytes of data + * @param[inout] _szOut A void* to save the reintcasts for the caller. */ + static inline void Swap4(void *_szOut) { + uint8_t *const szOut = reinterpret_cast<uint8_t *>(_szOut); + std::swap(szOut[0], szOut[3]); + std::swap(szOut[1], szOut[2]); + } + + // ---------------------------------------------------------------------- + /** Swap eight bytes of data + * @param[inout] _szOut A void* to save the reintcasts for the caller. */ + static inline void Swap8(void *_szOut) { + uint8_t *const szOut = reinterpret_cast<uint8_t *>(_szOut); + std::swap(szOut[0], szOut[7]); + std::swap(szOut[1], szOut[6]); + std::swap(szOut[2], szOut[5]); + std::swap(szOut[3], szOut[4]); + } + + // ---------------------------------------------------------------------- + /** ByteSwap a float. Not a joke. + * @param[inout] fOut ehm. .. */ + static inline void Swap(float *fOut) { + Swap4(fOut); + } + + // ---------------------------------------------------------------------- + /** ByteSwap a double. Not a joke. + * @param[inout] fOut ehm. .. */ + static inline void Swap(double *fOut) { + Swap8(fOut); + } + + // ---------------------------------------------------------------------- + /** ByteSwap an int16t. Not a joke. + * @param[inout] fOut ehm. .. */ + static inline void Swap(int16_t *fOut) { + Swap2(fOut); + } + + static inline void Swap(uint16_t *fOut) { + Swap2(fOut); + } + + // ---------------------------------------------------------------------- + /** ByteSwap an int32t. Not a joke. + * @param[inout] fOut ehm. .. */ + static inline void Swap(int32_t *fOut) { + Swap4(fOut); + } + + static inline void Swap(uint32_t *fOut) { + Swap4(fOut); + } + + // ---------------------------------------------------------------------- + /** ByteSwap an int64t. Not a joke. + * @param[inout] fOut ehm. .. */ + static inline void Swap(int64_t *fOut) { + Swap8(fOut); + } + + static inline void Swap(uint64_t *fOut) { + Swap8(fOut); + } + + // ---------------------------------------------------------------------- + //! Templatized ByteSwap + //! \returns param tOut as swapped + template <typename Type> + static inline Type Swapped(Type tOut) { + return _swapper<Type, sizeof(Type)>()(tOut); + } + +private: + template <typename T, size_t size> + struct _swapper; +}; + +template <typename T> +struct ByteSwap::_swapper<T, 2> { + T operator()(T tOut) { + Swap2(&tOut); + return tOut; + } +}; + +template <typename T> +struct ByteSwap::_swapper<T, 4> { + T operator()(T tOut) { + Swap4(&tOut); + return tOut; + } +}; + +template <typename T> +struct ByteSwap::_swapper<T, 8> { + T operator()(T tOut) { + Swap8(&tOut); + return tOut; + } +}; + +// -------------------------------------------------------------------------------------- +// ByteSwap macros for BigEndian/LittleEndian support +// -------------------------------------------------------------------------------------- +#if (defined AI_BUILD_BIG_ENDIAN) +#define AI_LE(t) (t) +#define AI_BE(t) ByteSwap::Swapped(t) +#define AI_LSWAP2(p) +#define AI_LSWAP4(p) +#define AI_LSWAP8(p) +#define AI_LSWAP2P(p) +#define AI_LSWAP4P(p) +#define AI_LSWAP8P(p) +#define LE_NCONST const +#define AI_SWAP2(p) ByteSwap::Swap2(&(p)) +#define AI_SWAP4(p) ByteSwap::Swap4(&(p)) +#define AI_SWAP8(p) ByteSwap::Swap8(&(p)) +#define AI_SWAP2P(p) ByteSwap::Swap2((p)) +#define AI_SWAP4P(p) ByteSwap::Swap4((p)) +#define AI_SWAP8P(p) ByteSwap::Swap8((p)) +#define BE_NCONST +#else +#define AI_BE(t) (t) +#define AI_LE(t) ByteSwap::Swapped(t) +#define AI_SWAP2(p) +#define AI_SWAP4(p) +#define AI_SWAP8(p) +#define AI_SWAP2P(p) +#define AI_SWAP4P(p) +#define AI_SWAP8P(p) +#define BE_NCONST const +#define AI_LSWAP2(p) ByteSwap::Swap2(&(p)) +#define AI_LSWAP4(p) ByteSwap::Swap4(&(p)) +#define AI_LSWAP8(p) ByteSwap::Swap8(&(p)) +#define AI_LSWAP2P(p) ByteSwap::Swap2((p)) +#define AI_LSWAP4P(p) ByteSwap::Swap4((p)) +#define AI_LSWAP8P(p) ByteSwap::Swap8((p)) +#define LE_NCONST +#endif + +namespace Intern { + +// -------------------------------------------------------------------------------------------- +template <typename T, bool doit> +struct ByteSwapper { + void operator()(T *inout) { + ByteSwap::Swap(inout); + } +}; + +template <typename T> +struct ByteSwapper<T, false> { + void operator()(T *) { + } +}; + +// -------------------------------------------------------------------------------------------- +template <bool SwapEndianess, typename T, bool RuntimeSwitch> +struct Getter { + void operator()(T *inout, bool le) { + le = !le; + if (le) { + ByteSwapper<T, (sizeof(T) > 1 ? true : false)>()(inout); + } else + ByteSwapper<T, false>()(inout); + } +}; + +template <bool SwapEndianess, typename T> +struct Getter<SwapEndianess, T, false> { + void operator()(T *inout, bool /*le*/) { + // static branch + ByteSwapper<T, (SwapEndianess && sizeof(T) > 1)>()(inout); + } +}; +} // namespace Intern +} // namespace FBXDocParser + +#endif // BYTE_SWAPPER_H diff --git a/modules/fbx/fbx_parser/CREDITS b/modules/fbx/fbx_parser/CREDITS new file mode 100644 index 0000000000..62b449614e --- /dev/null +++ b/modules/fbx/fbx_parser/CREDITS @@ -0,0 +1,183 @@ +=============================================================== +Open Asset Import Library (Assimp) +Developers and Contributors +=============================================================== + +The following is a non-exhaustive list of all constributors over the years. +If you think your name should be listed here, drop us a line and we'll add you. + +- Alexander Gessler, +3DS-, BLEND-, ASE-, DXF-, HMP-, MDL-, MD2-, MD3-, MD5-, MDC-, NFF-, PLY-, STL-, RAW-, OFF-, MS3D-, Q3D- and LWO-Loader, Assimp-Viewer, assimp-cmd, -noboost, Website (Design). + +- Thomas Schulze, +X-, Collada-, BVH-Loader, Postprocessing framework. Data structure & Interface design, documentation. + +- Kim Kulling, +Obj-, Q3BSD-, OpenGEX-Loader, Logging system, CMake-build-environment, Linux-build, Website ( Admin ), Coverity ( Admin ), Glitter ( Admin ). + +- R.Schmidt, +Linux build, eclipse support. + +- Matthias Gubisch, +Assimp.net +Visual Studio 9 support, bugfixes. + +- Mark Sibly +B3D-Loader, Assimp testing + +- Jonathan Klein +Ogre Loader, VC2010 fixes and CMake fixes. + +- Sebastian Hempel, +PyAssimp (first version) +Compile-Bugfixes for mingw, add environment for static library support in make. + +- Jonathan Pokrass +Supplied a bugfix concerning the scaling in the md3 loader. + +- Andrew Galante, +Submitted patches to make Assimp compile with GCC-4, a makefile and the xcode3 workspace. + +- Andreas Nagel +First Assimp testing & verification under Windows Vista 64 Bit. + +- Marius Schr�der +Allowed us to use many of his models for screenshots and testing. + +- Christian Schubert +Supplied various XFiles for testing purposes. + +- Tizian Wieland +Searched the web for hundreds of test models for internal use + +- John Connors +Supplied patches for linux and SCons. + +- T. R. +The GUY who performed some of the CSM mocaps. + +- Andy Maloney +Contributed fixes for the documentation and the doxygen markup + +- Zhao Lei +Contributed several bugfixes fixing memory leaks and improving float parsing + +- sueastside +Updated PyAssimp to the latest Assimp data structures and provided a script to keep the Python binding up-to-date. + +- Tobias Rittig +Collada testing with Cinema 4D + +- Brad Grantham +Improvements in OpenGL-Sample. + +- Robert Ramirez +Add group loading feature to Obj-Loader. + +- Chris Maiwald +Many bugreports, improving Assimp's portability, regular testing & feedback. + +- Stepan Hrbek +Bugreport and fix for a obj-materialloader crash. + +- David Nadlinger +D bindings, CMake install support. + +- Dario Accornero +Contributed several patches regarding Mac OS/XCode targets, bug reports. + +- Martin Walser (Samhayne) +Contributed the 'SimpleTexturedOpenGl' sample. + +- Matthias Fauconneau +Contributed a fix for the Q3-BSP loader. + +- Jørgen P. Tjernø +Contributed updated and improved xcode workspaces + +- drparallax +Contributed the /samples/SimpleAssimpViewX sample + +- Carsten Fuchs +Contributed a fix for the Normalize method in aiQuaternion. + +- dbburgess +Contributes a Android-specific build issue: log the hardware architecture for ARM. + +- alfiereinre7 +Contributes a obj-fileparser fix: missing tokens in the obj-token list. + +- Roman Kharitonov +Contributes a fix for the configure script environment. + +- Ed Diana +Contributed AssimpDelphi (/port/AssimpDelphi). + +- rdb +Contributes a bundle of fixes and improvements for the bsp-importer. + +- Mick P +For contributing the De-bone postprocessing step and filing various bug reports. + +- Rosen Diankov +Contributed patches to build assimp debian packages using cmake. + +- Mark Page +Contributed a patch to fix the VertexTriangleAdjacency postprocessing step. + +- IOhannes +Contributed the Debian build fixes ( architecture macro ). + +- gellule +Several LWO and LWS fixes (pivoting). + +- Marcel Metz +GCC/Linux fixes for the SimpleOpenGL sample. + +- Brian Miller +Bugfix for a compiler fix for iOS on arm. + +- Séverin Lemaignan +Rewrite of PyAssimp, distutils and Python3 support + +- albert-wang +Bugfixes for the collada parser + +- Ya ping Jin +Bugfixes for uv-tanget calculation. + +- Jonne Nauha +Ogre Binary format support + +- Filip Wasil, Tieto Poland Sp. z o.o. +Android JNI asset extraction support + +- Richard Steffen +Contributed ExportProperties interface +Contributed X File exporter +Contributed Step (stp) exporter + +- Thomas Iorns (mesilliac) +Initial FBX Export support + +For a more detailed list just check: https://github.com/assimp/assimp/network/members + + +======== +Patreons +======== + +Huge thanks to our Patreons! + +- migenius +- Marcus +- Cort +- elect +- Steffen + + +=================== +Commercial Sponsors +=================== + +- MyDidimo (mydidimo.com): Sponsored development of FBX Export support diff --git a/modules/fbx/fbx_parser/FBXAnimation.cpp b/modules/fbx/fbx_parser/FBXAnimation.cpp new file mode 100644 index 0000000000..b11e2c7f55 --- /dev/null +++ b/modules/fbx/fbx_parser/FBXAnimation.cpp @@ -0,0 +1,290 @@ +/*************************************************************************/ +/* FBXAnimation.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXAnimation.cpp + * @brief Assimp::FBX::AnimationCurve, Assimp::FBX::AnimationCurveNode, + * Assimp::FBX::AnimationLayer, Assimp::FBX::AnimationStack + */ + +#include "FBXCommon.h" +#include "FBXDocument.h" +#include "FBXDocumentUtil.h" +#include "FBXParser.h" + +namespace FBXDocParser { + +using namespace Util; + +// ------------------------------------------------------------------------------------------------ +AnimationCurve::AnimationCurve(uint64_t id, const ElementPtr element, const std::string &name, const Document & /*doc*/) : + Object(id, element, name) { + const ScopePtr sc = GetRequiredScope(element); + const ElementPtr KeyTime = GetRequiredElement(sc, "KeyTime"); + const ElementPtr KeyValueFloat = GetRequiredElement(sc, "KeyValueFloat"); + + // note preserved keys and values for legacy FBXConverter.cpp + // we can remove this once the animation system is written + // and clean up this code so we do not have copies everywhere. + ParseVectorDataArray(keys, KeyTime); + ParseVectorDataArray(values, KeyValueFloat); + + if (keys.size() != values.size()) { + DOMError("the number of key times does not match the number of keyframe values", KeyTime); + } + + // put the two lists into the map, underlying container is really just a dictionary + // these will always match, if not an error will throw and the file will not import + // this is useful because we then can report something and fix this later if it becomes an issue + // at this point we do not need a different count of these elements so this makes the + // most sense to do. + for (size_t x = 0; x < keys.size(); x++) { + keyvalues[keys[x]] = values[x]; + } + + const ElementPtr KeyAttrDataFloat = sc->GetElement("KeyAttrDataFloat"); + if (KeyAttrDataFloat) { + ParseVectorDataArray(attributes, KeyAttrDataFloat); + } + + const ElementPtr KeyAttrFlags = sc->GetElement("KeyAttrFlags"); + if (KeyAttrFlags) { + ParseVectorDataArray(flags, KeyAttrFlags); + } +} + +// ------------------------------------------------------------------------------------------------ +AnimationCurve::~AnimationCurve() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +AnimationCurveNode::AnimationCurveNode(uint64_t id, const ElementPtr element, const std::string &name, + const Document &doc, const char *const *target_prop_whitelist /*= NULL*/, + size_t whitelist_size /*= 0*/) : + Object(id, element, name), target(), doc(doc) { + const ScopePtr sc = GetRequiredScope(element); + + // find target node + const char *whitelist[] = { "Model", "NodeAttribute", "Deformer" }; + const std::vector<const Connection *> &conns = doc.GetConnectionsBySourceSequenced(ID(), whitelist, 3); + + for (const Connection *con : conns) { + // link should go for a property + if (!con->PropertyName().length()) { + continue; + } + + Object *object = con->DestinationObject(); + + if (!object) { + DOMWarning("failed to read destination object for AnimationCurveNode->Model link, ignoring", element); + continue; + } + + target = object; + prop = con->PropertyName(); + break; + } + + props = GetPropertyTable(doc, "AnimationCurveNode.FbxAnimCurveNode", element, sc, false); +} + +// ------------------------------------------------------------------------------------------------ +AnimationCurveNode::~AnimationCurveNode() { + curves.clear(); +} + +// ------------------------------------------------------------------------------------------------ +const AnimationMap &AnimationCurveNode::Curves() const { + /* Lazy loaded animation curves, will only load if required */ + if (curves.empty()) { + // resolve attached animation curves + const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "AnimationCurve"); + + for (const Connection *con : conns) { + // So the advantage of having this STL boilerplate is that it's dead simple once you get it. + // The other advantage is casting is guaranteed to be safe and nullptr will be returned in the last step if it fails. + Object *ob = con->SourceObject(); + AnimationCurve *anim_curve = dynamic_cast<AnimationCurve *>(ob); + ERR_CONTINUE_MSG(!anim_curve, "Failed to convert animation curve from object"); + + curves.insert(std::make_pair(con->PropertyName(), anim_curve)); + } + } + + return curves; +} + +// ------------------------------------------------------------------------------------------------ +AnimationLayer::AnimationLayer(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) : + Object(id, element, name), doc(doc) { + const ScopePtr sc = GetRequiredScope(element); + + // note: the props table here bears little importance and is usually absent + props = GetPropertyTable(doc, "AnimationLayer.FbxAnimLayer", element, sc, true); +} + +// ------------------------------------------------------------------------------------------------ +AnimationLayer::~AnimationLayer() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +const AnimationCurveNodeList AnimationLayer::Nodes(const char *const *target_prop_whitelist, + size_t whitelist_size /*= 0*/) const { + AnimationCurveNodeList nodes; + + // resolve attached animation nodes + const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "AnimationCurveNode"); + nodes.reserve(conns.size()); + + for (const Connection *con : conns) { + // link should not go to a property + if (con->PropertyName().length()) { + continue; + } + + Object *ob = con->SourceObject(); + + if (!ob) { + DOMWarning("failed to read source object for AnimationCurveNode->AnimationLayer link, ignoring", element); + continue; + } + + const AnimationCurveNode *anim = dynamic_cast<AnimationCurveNode *>(ob); + if (!anim) { + DOMWarning("source object for ->AnimationLayer link is not an AnimationCurveNode", element); + continue; + } + + if (target_prop_whitelist) { + const char *s = anim->TargetProperty().c_str(); + bool ok = false; + for (size_t i = 0; i < whitelist_size; ++i) { + if (!strcmp(s, target_prop_whitelist[i])) { + ok = true; + break; + } + } + if (!ok) { + continue; + } + } + nodes.push_back(anim); + } + + return nodes; +} + +// ------------------------------------------------------------------------------------------------ +AnimationStack::AnimationStack(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) : + Object(id, element, name) { + const ScopePtr sc = GetRequiredScope(element); + + // note: we don't currently use any of these properties so we shouldn't bother if it is missing + props = GetPropertyTable(doc, "AnimationStack.FbxAnimStack", element, sc, true); + + // resolve attached animation layers + const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "AnimationLayer"); + layers.reserve(conns.size()); + + for (const Connection *con : conns) { + // link should not go to a property + if (con->PropertyName().length()) { + continue; + } + + Object *ob = con->SourceObject(); + if (!ob) { + DOMWarning("failed to read source object for AnimationLayer->AnimationStack link, ignoring", element); + continue; + } + + const AnimationLayer *anim = dynamic_cast<const AnimationLayer *>(ob); + + if (!anim) { + DOMWarning("source object for ->AnimationStack link is not an AnimationLayer", element); + continue; + } + + layers.push_back(anim); + } +} + +// ------------------------------------------------------------------------------------------------ +AnimationStack::~AnimationStack() { + if (props != nullptr) { + delete props; + props = nullptr; + } +} +} // namespace FBXDocParser diff --git a/modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp b/modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp new file mode 100644 index 0000000000..1d2b7765c5 --- /dev/null +++ b/modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp @@ -0,0 +1,467 @@ +/*************************************************************************/ +/* FBXBinaryTokenizer.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/** @file FBXBinaryTokenizer.cpp + * @brief Implementation of a fake lexer for binary fbx files - + * we emit tokens so the parser needs almost no special handling + * for binary files. + */ + +#include "ByteSwapper.h" +#include "FBXTokenizer.h" +#include "core/string/print_string.h" + +#include <stdint.h> + +namespace FBXDocParser { +//enum Flag +//{ +// e_unknown_0 = 1 << 0, +// e_unknown_1 = 1 << 1, +// e_unknown_2 = 1 << 2, +// e_unknown_3 = 1 << 3, +// e_unknown_4 = 1 << 4, +// e_unknown_5 = 1 << 5, +// e_unknown_6 = 1 << 6, +// e_unknown_7 = 1 << 7, +// e_unknown_8 = 1 << 8, +// e_unknown_9 = 1 << 9, +// e_unknown_10 = 1 << 10, +// e_unknown_11 = 1 << 11, +// e_unknown_12 = 1 << 12, +// e_unknown_13 = 1 << 13, +// e_unknown_14 = 1 << 14, +// e_unknown_15 = 1 << 15, +// e_unknown_16 = 1 << 16, +// e_unknown_17 = 1 << 17, +// e_unknown_18 = 1 << 18, +// e_unknown_19 = 1 << 19, +// e_unknown_20 = 1 << 20, +// e_unknown_21 = 1 << 21, +// e_unknown_22 = 1 << 22, +// e_unknown_23 = 1 << 23, +// e_flag_field_size_64_bit = 1 << 24, // Not sure what is +// e_unknown_25 = 1 << 25, +// e_unknown_26 = 1 << 26, +// e_unknown_27 = 1 << 27, +// e_unknown_28 = 1 << 28, +// e_unknown_29 = 1 << 29, +// e_unknown_30 = 1 << 30, +// e_unknown_31 = 1 << 31 +//}; +// +//bool check_flag(uint32_t flags, Flag to_check) +//{ +// return (flags & to_check) != 0; +//} +// ------------------------------------------------------------------------------------------------ +Token::Token(const char *sbegin, const char *send, TokenType type, size_t offset) : + sbegin(sbegin), + send(send), + type(type), + line(offset), + column(BINARY_MARKER) { +#ifdef DEBUG_ENABLED + contents = std::string(sbegin, static_cast<size_t>(send - sbegin)); +#endif + // calc length + // measure from sBegin to sEnd and validate? +} + +namespace { + +// ------------------------------------------------------------------------------------------------ +// signal tokenization error +void TokenizeError(const std::string &message, size_t offset) { + print_error("[FBX-Tokenize] " + String(message.c_str()) + ", offset " + itos(offset)); +} + +// ------------------------------------------------------------------------------------------------ +size_t Offset(const char *begin, const char *cursor) { + //ai_assert(begin <= cursor); + + return cursor - begin; +} + +// ------------------------------------------------------------------------------------------------ +void TokenizeError(const std::string &message, const char *begin, const char *cursor) { + TokenizeError(message, Offset(begin, cursor)); +} + +// ------------------------------------------------------------------------------------------------ +uint32_t ReadWord(const char *input, const char *&cursor, const char *end) { + const size_t k_to_read = sizeof(uint32_t); + if (Offset(cursor, end) < k_to_read) { + TokenizeError("cannot ReadWord, out of bounds", input, cursor); + } + + uint32_t word; + ::memcpy(&word, cursor, 4); + AI_SWAP4(word); + + cursor += k_to_read; + + return word; +} + +// ------------------------------------------------------------------------------------------------ +uint64_t ReadDoubleWord(const char *input, const char *&cursor, const char *end) { + const size_t k_to_read = sizeof(uint64_t); + if (Offset(cursor, end) < k_to_read) { + TokenizeError("cannot ReadDoubleWord, out of bounds", input, cursor); + } + + uint64_t dword /*= *reinterpret_cast<const uint64_t*>(cursor)*/; + ::memcpy(&dword, cursor, sizeof(uint64_t)); + AI_SWAP8(dword); + + cursor += k_to_read; + + return dword; +} + +// ------------------------------------------------------------------------------------------------ +uint8_t ReadByte(const char *input, const char *&cursor, const char *end) { + if (Offset(cursor, end) < sizeof(uint8_t)) { + TokenizeError("cannot ReadByte, out of bounds", input, cursor); + } + + uint8_t word; /* = *reinterpret_cast< const uint8_t* >( cursor )*/ + ::memcpy(&word, cursor, sizeof(uint8_t)); + ++cursor; + + return word; +} + +// ------------------------------------------------------------------------------------------------ +unsigned int ReadString(const char *&sbegin_out, const char *&send_out, const char *input, + const char *&cursor, const char *end, bool long_length = false, bool allow_null = false) { + const uint32_t len_len = long_length ? 4 : 1; + if (Offset(cursor, end) < len_len) { + TokenizeError("cannot ReadString, out of bounds reading length", input, cursor); + } + + const uint32_t length = long_length ? ReadWord(input, cursor, end) : ReadByte(input, cursor, end); + + if (Offset(cursor, end) < length) { + TokenizeError("cannot ReadString, length is out of bounds", input, cursor); + } + + sbegin_out = cursor; + cursor += length; + + send_out = cursor; + + if (!allow_null) { + for (unsigned int i = 0; i < length; ++i) { + if (sbegin_out[i] == '\0') { + TokenizeError("failed ReadString, unexpected NUL character in string", input, cursor); + } + } + } + + return length; +} + +// ------------------------------------------------------------------------------------------------ +void ReadData(const char *&sbegin_out, const char *&send_out, const char *input, const char *&cursor, const char *end) { + if (Offset(cursor, end) < 1) { + TokenizeError("cannot ReadData, out of bounds reading length", input, cursor); + } + + const char type = *cursor; + sbegin_out = cursor++; + + switch (type) { + // 16 bit int + case 'Y': + cursor += 2; + break; + + // 1 bit bool flag (yes/no) + case 'C': + cursor += 1; + break; + + // 32 bit int + case 'I': + // <- fall through + + // float + case 'F': + cursor += 4; + break; + + // double + case 'D': + cursor += 8; + break; + + // 64 bit int + case 'L': + cursor += 8; + break; + + // note: do not write cursor += ReadWord(...cursor) as this would be UB + + // raw binary data + case 'R': { + const uint32_t length = ReadWord(input, cursor, end); + cursor += length; + break; + } + + case 'b': + // TODO: what is the 'b' type code? Right now we just skip over it / + // take the full range we could get + cursor = end; + break; + + // array of * + case 'f': + case 'd': + case 'l': + case 'i': + case 'c': { + const uint32_t length = ReadWord(input, cursor, end); + const uint32_t encoding = ReadWord(input, cursor, end); + + const uint32_t comp_len = ReadWord(input, cursor, end); + + // compute length based on type and check against the stored value + if (encoding == 0) { + uint32_t stride = 0; + switch (type) { + case 'f': + case 'i': + stride = 4; + break; + + case 'd': + case 'l': + stride = 8; + break; + + case 'c': + stride = 1; + break; + + default: + break; + }; + //ai_assert(stride > 0); + if (length * stride != comp_len) { + TokenizeError("cannot ReadData, calculated data stride differs from what the file claims", input, cursor); + } + } + // zip/deflate algorithm (encoding==1)? take given length. anything else? die + else if (encoding != 1) { + TokenizeError("cannot ReadData, unknown encoding", input, cursor); + } + cursor += comp_len; + break; + } + + // string + case 'S': { + const char *sb, *se; + // 0 characters can legally happen in such strings + ReadString(sb, se, input, cursor, end, true, true); + break; + } + default: + TokenizeError("cannot ReadData, unexpected type code: " + std::string(&type, 1), input, cursor); + } + + if (cursor > end) { + TokenizeError("cannot ReadData, the remaining size is too small for the data type: " + std::string(&type, 1), input, cursor); + } + + // the type code is contained in the returned range + send_out = cursor; +} + +// ------------------------------------------------------------------------------------------------ +bool ReadScope(TokenList &output_tokens, const char *input, const char *&cursor, const char *end, bool const is64bits) { + // the first word contains the offset at which this block ends + const uint64_t end_offset = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end); + + // we may get 0 if reading reached the end of the file - + // fbx files have a mysterious extra footer which I don't know + // how to extract any information from, but at least it always + // starts with a 0. + if (!end_offset) { + return false; + } + + if (end_offset > Offset(input, end)) { + TokenizeError("block offset is out of range", input, cursor); + } else if (end_offset < Offset(input, cursor)) { + TokenizeError("block offset is negative out of range", input, cursor); + } + + // the second data word contains the number of properties in the scope + const uint64_t prop_count = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end); + + // the third data word contains the length of the property list + const uint64_t prop_length = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end); + + // now comes the name of the scope/key + const char *sbeg, *send; + ReadString(sbeg, send, input, cursor, end); + + output_tokens.push_back(new_Token(sbeg, send, TokenType_KEY, Offset(input, cursor))); + + // now come the individual properties + const char *begin_cursor = cursor; + for (unsigned int i = 0; i < prop_count; ++i) { + ReadData(sbeg, send, input, cursor, begin_cursor + prop_length); + + output_tokens.push_back(new_Token(sbeg, send, TokenType_DATA, Offset(input, cursor))); + + if (i != prop_count - 1) { + output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_COMMA, Offset(input, cursor))); + } + } + + if (Offset(begin_cursor, cursor) != prop_length) { + TokenizeError("property length not reached, something is wrong", input, cursor); + } + + // at the end of each nested block, there is a NUL record to indicate + // that the sub-scope exists (i.e. to distinguish between P: and P : {}) + // this NUL record is 13 bytes long on 32 bit version and 25 bytes long on 64 bit. + const size_t sentinel_block_length = is64bits ? (sizeof(uint64_t) * 3 + 1) : (sizeof(uint32_t) * 3 + 1); + + if (Offset(input, cursor) < end_offset) { + if (end_offset - Offset(input, cursor) < sentinel_block_length) { + TokenizeError("insufficient padding bytes at block end", input, cursor); + } + + output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_OPEN_BRACKET, Offset(input, cursor))); + + // XXX this is vulnerable to stack overflowing .. + while (Offset(input, cursor) < end_offset - sentinel_block_length) { + ReadScope(output_tokens, input, cursor, input + end_offset - sentinel_block_length, is64bits); + } + output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_CLOSE_BRACKET, Offset(input, cursor))); + + for (unsigned int i = 0; i < sentinel_block_length; ++i) { + if (cursor[i] != '\0') { + TokenizeError("failed to read nested block sentinel, expected all bytes to be 0", input, cursor); + } + } + cursor += sentinel_block_length; + } + + if (Offset(input, cursor) != end_offset) { + TokenizeError("scope length not reached, something is wrong", input, cursor); + } + + return true; +} +} // anonymous namespace + +// ------------------------------------------------------------------------------------------------ +// TODO: Test FBX Binary files newer than the 7500 version to check if the 64 bits address behaviour is consistent +void TokenizeBinary(TokenList &output_tokens, const char *input, size_t length) { + if (length < 0x1b) { + //TokenizeError("file is too short",0); + } + + //uint32_t offset = 0x15; + /* const char* cursor = input + 0x15; + const uint32_t flags = ReadWord(input, cursor, input + length); + const uint8_t padding_0 = ReadByte(input, cursor, input + length); // unused + const uint8_t padding_1 = ReadByte(input, cursor, input + length); // unused*/ + + if (strncmp(input, "Kaydara FBX Binary", 18)) { + TokenizeError("magic bytes not found", 0); + } + + const char *cursor = input + 18; + /*Result ignored*/ ReadByte(input, cursor, input + length); + /*Result ignored*/ ReadByte(input, cursor, input + length); + /*Result ignored*/ ReadByte(input, cursor, input + length); + /*Result ignored*/ ReadByte(input, cursor, input + length); + /*Result ignored*/ ReadByte(input, cursor, input + length); + const uint32_t version = ReadWord(input, cursor, input + length); + print_verbose("FBX Version: " + itos(version)); + //ASSIMP_LOG_DEBUG_F("FBX version: ", version); + const bool is64bits = version >= 7500; + const char *end = input + length; + while (cursor < end) { + if (!ReadScope(output_tokens, input, cursor, input + length, is64bits)) { + break; + } + } +} +} // namespace FBXDocParser diff --git a/modules/fbx/fbx_parser/FBXCommon.h b/modules/fbx/fbx_parser/FBXCommon.h new file mode 100644 index 0000000000..641d6d351e --- /dev/null +++ b/modules/fbx/fbx_parser/FBXCommon.h @@ -0,0 +1,110 @@ +/*************************************************************************/ +/* FBXCommon.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXCommon.h +* Some useful constants and enums for dealing with FBX files. +*/ +#ifndef FBX_COMMON_H +#define FBX_COMMON_H + +#include <string> + +namespace FBXDocParser { +const std::string NULL_RECORD = { // 13 null bytes + '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0' +}; // who knows why +const std::string SEPARATOR = { '\x00', '\x01' }; // for use inside strings +const std::string MAGIC_NODE_TAG = "_$AssimpFbx$"; // from import +const int64_t SECOND = 46186158000; // FBX's kTime unit + +// rotation order. We'll probably use EulerXYZ for everything +enum RotOrder { + RotOrder_EulerXYZ = 0, + RotOrder_EulerXZY, + RotOrder_EulerYZX, + RotOrder_EulerYXZ, + RotOrder_EulerZXY, + RotOrder_EulerZYX, + + RotOrder_SphericXYZ, + + RotOrder_MAX // end-of-enum sentinel +}; + +enum TransformInheritance { + Transform_RrSs = 0, + Transform_RSrs = 1, + Transform_Rrs = 2, + TransformInheritance_MAX // end-of-enum sentinel +}; +} // namespace FBXDocParser + +#endif // FBX_COMMON_H diff --git a/modules/fbx/fbx_parser/FBXDeformer.cpp b/modules/fbx/fbx_parser/FBXDeformer.cpp new file mode 100644 index 0000000000..4b774e6b2a --- /dev/null +++ b/modules/fbx/fbx_parser/FBXDeformer.cpp @@ -0,0 +1,279 @@ +/*************************************************************************/ +/* FBXDeformer.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXNoteAttribute.cpp + * @brief Assimp::FBX::NodeAttribute (and subclasses) implementation + */ + +#include "FBXDocument.h" +#include "FBXDocumentUtil.h" +#include "FBXMeshGeometry.h" +#include "FBXParser.h" +#include "core/math/math_funcs.h" +#include "core/math/transform.h" + +#include <iostream> + +namespace FBXDocParser { + +using namespace Util; + +// ------------------------------------------------------------------------------------------------ +Deformer::Deformer(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : + Object(id, element, name) { + const ScopePtr sc = GetRequiredScope(element); + + const std::string &classname = ParseTokenAsString(GetRequiredToken(element, 2)); + props = GetPropertyTable(doc, "Deformer.Fbx" + classname, element, sc, true); +} + +// ------------------------------------------------------------------------------------------------ +Deformer::~Deformer() { +} + +Constraint::Constraint(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : + Object(id, element, name) { + const ScopePtr sc = GetRequiredScope(element); + const std::string &classname = ParseTokenAsString(GetRequiredToken(element, 2)); + // used something.fbx as this is a cache name. + props = GetPropertyTable(doc, "Something.Fbx" + classname, element, sc, true); +} + +Constraint::~Constraint() { +} + +// ------------------------------------------------------------------------------------------------ +Cluster::Cluster(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : + Deformer(id, element, doc, name), valid_transformAssociateModel(false) { + const ScopePtr sc = GetRequiredScope(element); + // for( auto element : sc.Elements()) + // { + // std::cout << "cluster element: " << element.first << std::endl; + // } + // + // element: Indexes + // element: Transform + // element: TransformAssociateModel + // element: TransformLink + // element: UserData + // element: Version + // element: Weights + + const ElementPtr Indexes = sc->GetElement("Indexes"); + const ElementPtr Weights = sc->GetElement("Weights"); + + const ElementPtr TransformAssociateModel = sc->GetElement("TransformAssociateModel"); + if (TransformAssociateModel != nullptr) { + //Transform t = ReadMatrix(*TransformAssociateModel); + link_mode = SkinLinkMode_Additive; + valid_transformAssociateModel = true; + } else { + link_mode = SkinLinkMode_Normalized; + valid_transformAssociateModel = false; + } + + const ElementPtr Transform = GetRequiredElement(sc, "Transform", element); + const ElementPtr TransformLink = GetRequiredElement(sc, "TransformLink", element); + + // todo: check if we need this + //const Element& TransformAssociateModel = GetRequiredElement(sc, "TransformAssociateModel", &element); + + transform = ReadMatrix(Transform); + transformLink = ReadMatrix(TransformLink); + + // it is actually possible that there be Deformer's with no weights + if (!!Indexes != !!Weights) { + DOMError("either Indexes or Weights are missing from Cluster", element); + } + + if (Indexes) { + ParseVectorDataArray(indices, Indexes); + ParseVectorDataArray(weights, Weights); + } + + if (indices.size() != weights.size()) { + DOMError("sizes of index and weight array don't match up", element); + } + + // read assigned node + const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Model"); + for (const Connection *con : conns) { + const Model *mod = ProcessSimpleConnection<Model>(*con, false, "Model -> Cluster", element); + if (mod) { + node = mod; + break; + } + } + + if (!node) { + DOMError("failed to read target Node for Cluster", element); + node = nullptr; + } +} + +// ------------------------------------------------------------------------------------------------ +Cluster::~Cluster() { +} + +// ------------------------------------------------------------------------------------------------ +Skin::Skin(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : + Deformer(id, element, doc, name), accuracy(0.0f) { + const ScopePtr sc = GetRequiredScope(element); + + // keep this it is used for debugging and any FBX format changes + // for (auto element : sc.Elements()) { + // std::cout << "skin element: " << element.first << std::endl; + // } + + const ElementPtr Link_DeformAcuracy = sc->GetElement("Link_DeformAcuracy"); + if (Link_DeformAcuracy) { + accuracy = ParseTokenAsFloat(GetRequiredToken(Link_DeformAcuracy, 0)); + } + + const ElementPtr SkinType = sc->GetElement("SkinningType"); + + if (SkinType) { + std::string skin_type = ParseTokenAsString(GetRequiredToken(SkinType, 0)); + + if (skin_type == "Linear") { + skinType = Skin_Linear; + } else if (skin_type == "Rigid") { + skinType = Skin_Rigid; + } else if (skin_type == "DualQuaternion") { + skinType = Skin_DualQuaternion; + } else if (skin_type == "Blend") { + skinType = Skin_Blend; + } else { + print_error("[doc:skin] could not find valid skin type: " + String(skin_type.c_str())); + } + } + + // resolve assigned clusters + const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Deformer"); + + // + + clusters.reserve(conns.size()); + for (const Connection *con : conns) { + const Cluster *cluster = ProcessSimpleConnection<Cluster>(*con, false, "Cluster -> Skin", element); + if (cluster) { + clusters.push_back(cluster); + continue; + } + } +} + +// ------------------------------------------------------------------------------------------------ +Skin::~Skin() { +} +// ------------------------------------------------------------------------------------------------ +BlendShape::BlendShape(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : + Deformer(id, element, doc, name) { + const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Deformer"); + blendShapeChannels.reserve(conns.size()); + for (const Connection *con : conns) { + const BlendShapeChannel *bspc = ProcessSimpleConnection<BlendShapeChannel>(*con, false, "BlendShapeChannel -> BlendShape", element); + if (bspc) { + blendShapeChannels.push_back(bspc); + continue; + } + } +} +// ------------------------------------------------------------------------------------------------ +BlendShape::~BlendShape() { +} +// ------------------------------------------------------------------------------------------------ +BlendShapeChannel::BlendShapeChannel(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : + Deformer(id, element, doc, name) { + const ScopePtr sc = GetRequiredScope(element); + const ElementPtr DeformPercent = sc->GetElement("DeformPercent"); + if (DeformPercent) { + percent = ParseTokenAsFloat(GetRequiredToken(DeformPercent, 0)); + } + const ElementPtr FullWeights = sc->GetElement("FullWeights"); + if (FullWeights) { + ParseVectorDataArray(fullWeights, FullWeights); + } + const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Geometry"); + shapeGeometries.reserve(conns.size()); + for (const Connection *con : conns) { + const ShapeGeometry *const sg = ProcessSimpleConnection<ShapeGeometry>(*con, false, "Shape -> BlendShapeChannel", element); + if (sg) { + shapeGeometries.push_back(sg); + continue; + } + } +} +// ------------------------------------------------------------------------------------------------ +BlendShapeChannel::~BlendShapeChannel() { +} +// ------------------------------------------------------------------------------------------------ +} // namespace FBXDocParser diff --git a/modules/fbx/fbx_parser/FBXDocument.cpp b/modules/fbx/fbx_parser/FBXDocument.cpp new file mode 100644 index 0000000000..bcf7fa1565 --- /dev/null +++ b/modules/fbx/fbx_parser/FBXDocument.cpp @@ -0,0 +1,713 @@ +/*************************************************************************/ +/* FBXDocument.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the* + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXDocument.cpp + * @brief Implementation of the FBX DOM classes + */ + +#include "FBXDocument.h" +#include "FBXDocumentUtil.h" +#include "FBXImportSettings.h" +#include "FBXMeshGeometry.h" +#include "FBXParser.h" +#include "FBXProperties.h" +#include "FBXUtil.h" + +#include <algorithm> +#include <functional> +#include <iostream> +#include <map> +#include <memory> + +namespace FBXDocParser { + +using namespace Util; + +// ------------------------------------------------------------------------------------------------ +LazyObject::LazyObject(uint64_t id, const ElementPtr element, const Document &doc) : + doc(doc), element(element), id(id), flags() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +LazyObject::~LazyObject() { + object.reset(); +} + +ObjectPtr LazyObject::LoadObject() { + if (IsBeingConstructed() || FailedToConstruct()) { + return nullptr; + } + + if (object) { + return object.get(); + } + + TokenPtr key = element->KeyToken(); + ERR_FAIL_COND_V(!key, nullptr); + const TokenList &tokens = element->Tokens(); + + if (tokens.size() < 3) { + //DOMError("expected at least 3 tokens: id, name and class tag",&element); + return nullptr; + } + + const char *err = nullptr; + std::string name = ParseTokenAsString(tokens[1], err); + if (err) { + DOMError(err, element); + } + + // small fix for binary reading: binary fbx files don't use + // prefixes such as Model:: in front of their names. The + // loading code expects this at many places, though! + // so convert the binary representation (a 0x0001) to the + // double colon notation. + if (tokens[1]->IsBinary()) { + for (size_t i = 0; i < name.length(); ++i) { + if (name[i] == 0x0 && name[i + 1] == 0x1) { + name = name.substr(i + 2) + "::" + name.substr(0, i); + } + } + } + + const std::string classtag = ParseTokenAsString(tokens[2], err); + if (err) { + DOMError(err, element); + } + + // prevent recursive calls + flags |= BEING_CONSTRUCTED; + + // this needs to be relatively fast since it happens a lot, + // so avoid constructing strings all the time. + const char *obtype = key->begin(); + const size_t length = static_cast<size_t>(key->end() - key->begin()); + + if (!strncmp(obtype, "Pose", length)) { + object.reset(new FbxPose(id, element, doc, name)); + } else if (!strncmp(obtype, "Geometry", length)) { + if (!strcmp(classtag.c_str(), "Mesh")) { + object.reset(new MeshGeometry(id, element, name, doc)); + } + if (!strcmp(classtag.c_str(), "Shape")) { + object.reset(new ShapeGeometry(id, element, name, doc)); + } + if (!strcmp(classtag.c_str(), "Line")) { + object.reset(new LineGeometry(id, element, name, doc)); + } + } else if (!strncmp(obtype, "NodeAttribute", length)) { + if (!strcmp(classtag.c_str(), "Camera")) { + object.reset(new Camera(id, element, doc, name)); + } else if (!strcmp(classtag.c_str(), "CameraSwitcher")) { + object.reset(new CameraSwitcher(id, element, doc, name)); + } else if (!strcmp(classtag.c_str(), "Light")) { + object.reset(new Light(id, element, doc, name)); + } else if (!strcmp(classtag.c_str(), "Null")) { + object.reset(new Null(id, element, doc, name)); + } else if (!strcmp(classtag.c_str(), "LimbNode")) { + // This is an older format for bones + // this is what blender uses I believe + object.reset(new LimbNode(id, element, doc, name)); + } + } else if (!strncmp(obtype, "Constraint", length)) { + object.reset(new Constraint(id, element, doc, name)); + } else if (!strncmp(obtype, "Deformer", length)) { + if (!strcmp(classtag.c_str(), "Cluster")) { + object.reset(new Cluster(id, element, doc, name)); + } else if (!strcmp(classtag.c_str(), "Skin")) { + object.reset(new Skin(id, element, doc, name)); + } else if (!strcmp(classtag.c_str(), "BlendShape")) { + object.reset(new BlendShape(id, element, doc, name)); + } else if (!strcmp(classtag.c_str(), "BlendShapeChannel")) { + object.reset(new BlendShapeChannel(id, element, doc, name)); + } + } else if (!strncmp(obtype, "Model", length)) { + // Model is normal node + + // LimbNode model is a 'bone' node. + if (!strcmp(classtag.c_str(), "LimbNode")) { + object.reset(new ModelLimbNode(id, element, doc, name)); + + } else if (strcmp(classtag.c_str(), "IKEffector") && strcmp(classtag.c_str(), "FKEffector")) { + // FK and IK effectors are not supporte + object.reset(new Model(id, element, doc, name)); + } + } else if (!strncmp(obtype, "Material", length)) { + object.reset(new Material(id, element, doc, name)); + } else if (!strncmp(obtype, "Texture", length)) { + object.reset(new Texture(id, element, doc, name)); + } else if (!strncmp(obtype, "LayeredTexture", length)) { + object.reset(new LayeredTexture(id, element, doc, name)); + } else if (!strncmp(obtype, "Video", length)) { + object.reset(new Video(id, element, doc, name)); + } else if (!strncmp(obtype, "AnimationStack", length)) { + object.reset(new AnimationStack(id, element, name, doc)); + } else if (!strncmp(obtype, "AnimationLayer", length)) { + object.reset(new AnimationLayer(id, element, name, doc)); + } else if (!strncmp(obtype, "AnimationCurve", length)) { + object.reset(new AnimationCurve(id, element, name, doc)); + } else if (!strncmp(obtype, "AnimationCurveNode", length)) { + object.reset(new AnimationCurveNode(id, element, name, doc)); + } else { + ERR_FAIL_V_MSG(nullptr, "FBX contains unsupported object: " + String(obtype)); + } + + flags &= ~BEING_CONSTRUCTED; + + return object.get(); +} + +// ------------------------------------------------------------------------------------------------ +Object::Object(uint64_t id, const ElementPtr element, const std::string &name) : + element(element), name(name), id(id) { +} + +// ------------------------------------------------------------------------------------------------ +Object::~Object() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +FileGlobalSettings::FileGlobalSettings(const Document &doc, const PropertyTable *props) : + props(props), doc(doc) { + // empty +} + +// ------------------------------------------------------------------------------------------------ +FileGlobalSettings::~FileGlobalSettings() { + if (props != nullptr) { + delete props; + props = nullptr; + } +} + +// ------------------------------------------------------------------------------------------------ +Document::Document(const Parser &parser, const ImportSettings &settings) : + settings(settings), parser(parser), SafeToImport(false) { + // Cannot use array default initialization syntax because vc8 fails on it + for (unsigned int &timeStamp : creationTimeStamp) { + timeStamp = 0; + } + + // we must check if we can read the header version safely, if its outdated then drop it. + if (ReadHeader()) { + SafeToImport = true; + ReadPropertyTemplates(); + + ReadGlobalSettings(); + + // This order is important, connections need parsed objects to check + // whether connections are ok or not. Objects may not be evaluated yet, + // though, since this may require valid connections. + ReadObjects(); + ReadConnections(); + } +} + +// ------------------------------------------------------------------------------------------------ +Document::~Document() { + for (PropertyTemplateMap::value_type v : templates) { + delete v.second; + } + + for (ObjectMap::value_type &v : objects) { + delete v.second; + } + + for (ConnectionMap::value_type &v : src_connections) { + delete v.second; + } + + if (metadata_properties != nullptr) { + delete metadata_properties; + } + // clear globals import pointer + globals.reset(); +} + +// ------------------------------------------------------------------------------------------------ +static const unsigned int LowerSupportedVersion = 7300; +static const unsigned int UpperSupportedVersion = 7700; + +bool Document::ReadHeader() { + // Read ID objects from "Objects" section + ScopePtr sc = parser.GetRootScope(); + ElementPtr ehead = sc->GetElement("FBXHeaderExtension"); + if (!ehead || !ehead->Compound()) { + DOMError("no FBXHeaderExtension dictionary found"); + } + + const ScopePtr shead = ehead->Compound(); + fbxVersion = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(shead, "FBXVersion", ehead), 0)); + + // While we may have some success with newer files, we don't support + // the older 6.n fbx format + if (fbxVersion < LowerSupportedVersion) { + DOMWarning("unsupported, old format version, FBX 2015-2020, you must re-export in a more modern version of your original modelling application"); + return false; + } + if (fbxVersion > UpperSupportedVersion) { + DOMWarning("unsupported, newer format version, supported are only FBX 2015, up to FBX 2020" + " trying to read it nevertheless"); + } + + const ElementPtr ecreator = shead->GetElement("Creator"); + if (ecreator) { + creator = ParseTokenAsString(GetRequiredToken(ecreator, 0)); + } + + // + // Scene Info + // + + const ElementPtr scene_info = shead->GetElement("SceneInfo"); + + if (scene_info) { + PropertyTable *fileExportProps = const_cast<PropertyTable *>(GetPropertyTable(*this, "", scene_info, scene_info->Compound(), true)); + + if (fileExportProps) { + metadata_properties = fileExportProps; + } + } + + const ElementPtr etimestamp = shead->GetElement("CreationTimeStamp"); + if (etimestamp && etimestamp->Compound()) { + const ScopePtr stimestamp = etimestamp->Compound(); + creationTimeStamp[0] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Year"), 0)); + creationTimeStamp[1] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Month"), 0)); + creationTimeStamp[2] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Day"), 0)); + creationTimeStamp[3] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Hour"), 0)); + creationTimeStamp[4] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Minute"), 0)); + creationTimeStamp[5] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Second"), 0)); + creationTimeStamp[6] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Millisecond"), 0)); + } + + return true; +} + +// ------------------------------------------------------------------------------------------------ +void Document::ReadGlobalSettings() { + ERR_FAIL_COND_MSG(globals != nullptr, "Global settings is already setup this is a serious error and should be reported"); + + const ScopePtr sc = parser.GetRootScope(); + const ElementPtr ehead = sc->GetElement("GlobalSettings"); + if (nullptr == ehead || !ehead->Compound()) { + DOMWarning("no GlobalSettings dictionary found"); + globals = std::make_shared<FileGlobalSettings>(*this, new PropertyTable()); + return; + } + + const PropertyTable *props = GetPropertyTable(*this, "", ehead, ehead->Compound(), true); + + //double v = PropertyGet<float>( *props, std::string("UnitScaleFactor"), 1.0 ); + + if (!props) { + DOMError("GlobalSettings dictionary contains no property table"); + } + + globals = std::make_shared<FileGlobalSettings>(*this, props); +} + +// ------------------------------------------------------------------------------------------------ +void Document::ReadObjects() { + // read ID objects from "Objects" section + const ScopePtr sc = parser.GetRootScope(); + const ElementPtr eobjects = sc->GetElement("Objects"); + if (!eobjects || !eobjects->Compound()) { + DOMError("no Objects dictionary found"); + } + + // add a dummy entry to represent the Model::RootNode object (id 0), + // which is only indirectly defined in the input file + objects[0] = new LazyObject(0L, eobjects, *this); + + const ScopePtr sobjects = eobjects->Compound(); + for (const ElementMap::value_type &iter : sobjects->Elements()) { + // extract ID + const TokenList &tok = iter.second->Tokens(); + + if (tok.empty()) { + DOMError("expected ID after object key", iter.second); + } + + const char *err; + const uint64_t id = ParseTokenAsID(tok[0], err); + if (err) { + DOMError(err, iter.second); + } + + // id=0 is normally implicit + if (id == 0L) { + DOMError("encountered object with implicitly defined id 0", iter.second); + } + + if (objects.find(id) != objects.end()) { + DOMWarning("encountered duplicate object id, ignoring first occurrence", iter.second); + } + + objects[id] = new LazyObject(id, iter.second, *this); + + // grab all animation stacks upfront since there is no listing of them + if (!strcmp(iter.first.c_str(), "AnimationStack")) { + animationStacks.push_back(id); + } else if (!strcmp(iter.first.c_str(), "Constraint")) { + constraints.push_back(id); + } else if (!strcmp(iter.first.c_str(), "Pose")) { + bind_poses.push_back(id); + } else if (!strcmp(iter.first.c_str(), "Material")) { + materials.push_back(id); + } else if (!strcmp(iter.first.c_str(), "Deformer")) { + TokenPtr key = iter.second->KeyToken(); + ERR_CONTINUE_MSG(!key, "[parser bug] invalid token key for deformer"); + const TokenList &tokens = iter.second->Tokens(); + const std::string class_tag = ParseTokenAsString(tokens[2], err); + + if (err) { + DOMError(err, iter.second); + } + + if (class_tag == "Skin") { + //print_verbose("registered skin:" + itos(id)); + skins.push_back(id); + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +void Document::ReadPropertyTemplates() { + const ScopePtr sc = parser.GetRootScope(); + // read property templates from "Definitions" section + const ElementPtr edefs = sc->GetElement("Definitions"); + if (!edefs || !edefs->Compound()) { + DOMWarning("no Definitions dictionary found"); + return; + } + + const ScopePtr sdefs = edefs->Compound(); + const ElementCollection otypes = sdefs->GetCollection("ObjectType"); + for (ElementMap::const_iterator it = otypes.first; it != otypes.second; ++it) { + const ElementPtr el = (*it).second; + const ScopePtr sc_2 = el->Compound(); + if (!sc_2) { + DOMWarning("expected nested scope in ObjectType, ignoring", el); + continue; + } + + const TokenList &tok = el->Tokens(); + if (tok.empty()) { + DOMWarning("expected name for ObjectType element, ignoring", el); + continue; + } + + const std::string &oname = ParseTokenAsString(tok[0]); + + const ElementCollection templs = sc_2->GetCollection("PropertyTemplate"); + for (ElementMap::const_iterator iter = templs.first; iter != templs.second; ++iter) { + const ElementPtr el_2 = (*iter).second; + const ScopePtr sc_3 = el_2->Compound(); + if (!sc_3) { + DOMWarning("expected nested scope in PropertyTemplate, ignoring", el); + continue; + } + + const TokenList &tok_2 = el_2->Tokens(); + if (tok_2.empty()) { + DOMWarning("expected name for PropertyTemplate element, ignoring", el); + continue; + } + + const std::string &pname = ParseTokenAsString(tok_2[0]); + + const ElementPtr Properties70 = sc_3->GetElement("Properties70"); + if (Properties70) { + // PropertyTable(const ElementPtr element, const PropertyTable* templateProps); + const PropertyTable *props = new PropertyTable(Properties70, nullptr); + + templates[oname + "." + pname] = props; + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +void Document::ReadConnections() { + const ScopePtr sc = parser.GetRootScope(); + + // read property templates from "Definitions" section + const ElementPtr econns = sc->GetElement("Connections"); + if (!econns || !econns->Compound()) { + DOMError("no Connections dictionary found"); + } + + uint64_t insertionOrder = 0l; + const ScopePtr sconns = econns->Compound(); + const ElementCollection conns = sconns->GetCollection("C"); + for (ElementMap::const_iterator it = conns.first; it != conns.second; ++it) { + const ElementPtr el = (*it).second; + const std::string &type = ParseTokenAsString(GetRequiredToken(el, 0)); + + // PP = property-property connection, ignored for now + // (tokens: "PP", ID1, "Property1", ID2, "Property2") + if (type == "PP") { + continue; + } + + const uint64_t src = ParseTokenAsID(GetRequiredToken(el, 1)); + const uint64_t dest = ParseTokenAsID(GetRequiredToken(el, 2)); + + // OO = object-object connection + // OP = object-property connection, in which case the destination property follows the object ID + const std::string &prop = (type == "OP" ? ParseTokenAsString(GetRequiredToken(el, 3)) : ""); + + if (objects.find(src) == objects.end()) { + DOMWarning("source object for connection does not exist", el); + continue; + } + + // dest may be 0 (root node) but we added a dummy object before + if (objects.find(dest) == objects.end()) { + DOMWarning("destination object for connection does not exist", el); + continue; + } + + // add new connection + const Connection *const c = new Connection(insertionOrder++, src, dest, prop, *this); + src_connections.insert(ConnectionMap::value_type(src, c)); + dest_connections.insert(ConnectionMap::value_type(dest, c)); + } +} + +// ------------------------------------------------------------------------------------------------ +const std::vector<const AnimationStack *> &Document::AnimationStacks() const { + if (!animationStacksResolved.empty() || animationStacks.empty()) { + return animationStacksResolved; + } + + animationStacksResolved.reserve(animationStacks.size()); + for (uint64_t id : animationStacks) { + LazyObject *lazy = GetObject(id); + + // Two things happen here: + // We cast internally an Object PTR to an Animation Stack PTR + // We return invalid weak_ptrs for objects which are invalid + + const AnimationStack *stack = lazy->Get<AnimationStack>(); + ERR_CONTINUE_MSG(!stack, "invalid ptr to AnimationStack - conversion failure"); + + // We push back the weak reference :) to keep things simple, as ownership is on the parser side so it wont be cleaned up. + animationStacksResolved.push_back(stack); + } + + return animationStacksResolved; +} + +// ------------------------------------------------------------------------------------------------ +LazyObject *Document::GetObject(uint64_t id) const { + ObjectMap::const_iterator it = objects.find(id); + return it == objects.end() ? nullptr : (*it).second; +} + +#define MAX_CLASSNAMES 6 + +// ------------------------------------------------------------------------------------------------ +std::vector<const Connection *> Document::GetConnectionsSequenced(uint64_t id, const ConnectionMap &conns) const { + std::vector<const Connection *> temp; + + const std::pair<ConnectionMap::const_iterator, ConnectionMap::const_iterator> range = + conns.equal_range(id); + + temp.reserve(std::distance(range.first, range.second)); + for (ConnectionMap::const_iterator it = range.first; it != range.second; ++it) { + temp.push_back((*it).second); + } + + std::sort(temp.begin(), temp.end(), std::mem_fn(&Connection::Compare)); + + return temp; // NRVO should handle this +} + +// ------------------------------------------------------------------------------------------------ +std::vector<const Connection *> Document::GetConnectionsSequenced(uint64_t id, bool is_src, + const ConnectionMap &conns, + const char *const *classnames, + size_t count) const + +{ + size_t lengths[MAX_CLASSNAMES]; + + const size_t c = count; + for (size_t i = 0; i < c; ++i) { + lengths[i] = strlen(classnames[i]); + } + + std::vector<const Connection *> temp; + const std::pair<ConnectionMap::const_iterator, ConnectionMap::const_iterator> range = + conns.equal_range(id); + + temp.reserve(std::distance(range.first, range.second)); + for (ConnectionMap::const_iterator it = range.first; it != range.second; ++it) { + TokenPtr key = (is_src ? (*it).second->LazyDestinationObject() : (*it).second->LazySourceObject())->GetElement()->KeyToken(); + + const char *obtype = key->begin(); + + for (size_t i = 0; i < c; ++i) { + //ai_assert(classnames[i]); + if (static_cast<size_t>(std::distance(key->begin(), key->end())) == lengths[i] && !strncmp(classnames[i], obtype, lengths[i])) { + obtype = nullptr; + break; + } + } + + if (obtype) { + continue; + } + + temp.push_back((*it).second); + } + + std::sort(temp.begin(), temp.end(), std::mem_fn(&Connection::Compare)); + return temp; // NRVO should handle this +} + +// ------------------------------------------------------------------------------------------------ +std::vector<const Connection *> Document::GetConnectionsBySourceSequenced(uint64_t source) const { + return GetConnectionsSequenced(source, ConnectionsBySource()); +} + +// ------------------------------------------------------------------------------------------------ +std::vector<const Connection *> Document::GetConnectionsBySourceSequenced(uint64_t src, const char *classname) const { + const char *arr[] = { classname }; + return GetConnectionsBySourceSequenced(src, arr, 1); +} + +// ------------------------------------------------------------------------------------------------ +std::vector<const Connection *> Document::GetConnectionsBySourceSequenced(uint64_t source, + const char *const *classnames, size_t count) const { + return GetConnectionsSequenced(source, true, ConnectionsBySource(), classnames, count); +} + +// ------------------------------------------------------------------------------------------------ +std::vector<const Connection *> Document::GetConnectionsByDestinationSequenced(uint64_t dest, + const char *classname) const { + const char *arr[] = { classname }; + return GetConnectionsByDestinationSequenced(dest, arr, 1); +} + +// ------------------------------------------------------------------------------------------------ +std::vector<const Connection *> Document::GetConnectionsByDestinationSequenced(uint64_t dest) const { + return GetConnectionsSequenced(dest, ConnectionsByDestination()); +} + +// ------------------------------------------------------------------------------------------------ +std::vector<const Connection *> Document::GetConnectionsByDestinationSequenced(uint64_t dest, + const char *const *classnames, size_t count) const { + return GetConnectionsSequenced(dest, false, ConnectionsByDestination(), classnames, count); +} + +// ------------------------------------------------------------------------------------------------ +Connection::Connection(uint64_t insertionOrder, uint64_t src, uint64_t dest, const std::string &prop, + const Document &doc) : + insertionOrder(insertionOrder), prop(prop), src(src), dest(dest), doc(doc) { +} + +// ------------------------------------------------------------------------------------------------ +Connection::~Connection() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +LazyObject *Connection::LazySourceObject() const { + LazyObject *const lazy = doc.GetObject(src); + return lazy; +} + +// ------------------------------------------------------------------------------------------------ +LazyObject *Connection::LazyDestinationObject() const { + LazyObject *const lazy = doc.GetObject(dest); + return lazy; +} + +// ------------------------------------------------------------------------------------------------ +Object *Connection::SourceObject() const { + LazyObject *lazy = doc.GetObject(src); + //ai_assert(lazy); + return lazy->LoadObject(); +} + +// ------------------------------------------------------------------------------------------------ +Object *Connection::DestinationObject() const { + LazyObject *lazy = doc.GetObject(dest); + //ai_assert(lazy); + return lazy->LoadObject(); +} +} // namespace FBXDocParser diff --git a/modules/fbx/fbx_parser/FBXDocument.h b/modules/fbx/fbx_parser/FBXDocument.h new file mode 100644 index 0000000000..b810197d7e --- /dev/null +++ b/modules/fbx/fbx_parser/FBXDocument.h @@ -0,0 +1,1319 @@ +/*************************************************************************/ +/* FBXDocument.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/** @file FBXDocument.h + * @brief FBX DOM + */ +#ifndef FBX_DOCUMENT_H +#define FBX_DOCUMENT_H + +#include "FBXCommon.h" +#include "FBXParser.h" +#include "FBXProperties.h" +#include "core/math/transform.h" +#include "core/math/vector2.h" +#include "core/math/vector3.h" +#include "core/string/print_string.h" +#include <stdint.h> +#include <numeric> + +#define _AI_CONCAT(a, b) a##b +#define AI_CONCAT(a, b) _AI_CONCAT(a, b) + +namespace FBXDocParser { + +class Parser; +class Object; +struct ImportSettings; +class Connection; + +class PropertyTable; +class Document; +class Material; +class ShapeGeometry; +class LineGeometry; +class Geometry; + +class Video; + +class AnimationCurve; +class AnimationCurveNode; +class AnimationLayer; +class AnimationStack; + +class BlendShapeChannel; +class BlendShape; +class Skin; +class Cluster; + +typedef Object *ObjectPtr; +#define new_Object new Object + +/** Represents a delay-parsed FBX objects. Many objects in the scene + * are not needed by assimp, so it makes no sense to parse them + * upfront. */ +class LazyObject { +public: + LazyObject(uint64_t id, const ElementPtr element, const Document &doc); + ~LazyObject(); + + ObjectPtr LoadObject(); + + /* Casting weak pointers to their templated type safely and preserving ref counting and safety + * with lock() keyword to prevent leaking memory + */ + template <typename T> + const T *Get() { + ObjectPtr ob = LoadObject(); + return dynamic_cast<const T *>(ob); + } + + uint64_t ID() const { + return id; + } + + bool IsBeingConstructed() const { + return (flags & BEING_CONSTRUCTED) != 0; + } + + bool FailedToConstruct() const { + return (flags & FAILED_TO_CONSTRUCT) != 0; + } + + ElementPtr GetElement() const { + return element; + } + + const Document &GetDocument() const { + return doc; + } + +private: + const Document &doc; + ElementPtr element = nullptr; + std::shared_ptr<Object> object = nullptr; + const uint64_t id = 0; + + enum Flags { + BEING_CONSTRUCTED = 0x1, + FAILED_TO_CONSTRUCT = 0x2 + }; + + unsigned int flags = 0; +}; + +/** Base class for in-memory (DOM) representations of FBX objects */ +class Object { +public: + Object(uint64_t id, const ElementPtr element, const std::string &name); + + virtual ~Object(); + + ElementPtr SourceElement() const { + return element; + } + + const std::string &Name() const { + return name; + } + + uint64_t ID() const { + return id; + } + +protected: + const ElementPtr element; + const std::string name; + const uint64_t id = 0; +}; + +/** DOM class for generic FBX NoteAttribute blocks. NoteAttribute's just hold a property table, + * fixed members are added by deriving classes. */ +class NodeAttribute : public Object { +public: + NodeAttribute(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); + + virtual ~NodeAttribute(); + + const PropertyTable *Props() const { + return props; + } + +private: + const PropertyTable *props; +}; + +/** DOM base class for FBX camera settings attached to a node */ +class CameraSwitcher : public NodeAttribute { +public: + CameraSwitcher(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); + + virtual ~CameraSwitcher(); + + int CameraID() const { + return cameraId; + } + + const std::string &CameraName() const { + return cameraName; + } + + const std::string &CameraIndexName() const { + return cameraIndexName; + } + +private: + int cameraId; + std::string cameraName; + std::string cameraIndexName; +}; + +#define fbx_stringize(a) #a + +#define fbx_simple_property(name, type, default_value) \ + type name() const { \ + return PropertyGet<type>(Props(), fbx_stringize(name), (default_value)); \ + } + +// XXX improve logging +#define fbx_simple_enum_property(name, type, default_value) \ + type name() const { \ + const int ival = PropertyGet<int>(Props(), fbx_stringize(name), static_cast<int>(default_value)); \ + if (ival < 0 || ival >= AI_CONCAT(type, _MAX)) { \ + return static_cast<type>(default_value); \ + } \ + return static_cast<type>(ival); \ + } + +class FbxPoseNode; +class FbxPose : public Object { +public: + FbxPose(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); + + const std::vector<FbxPoseNode *> &GetBindPoses() const { + return pose_nodes; + } + + virtual ~FbxPose(); + +private: + std::vector<FbxPoseNode *> pose_nodes; +}; + +class FbxPoseNode { +public: + FbxPoseNode(const ElementPtr element, const Document &doc, const std::string &name) { + const ScopePtr sc = GetRequiredScope(element); + + // get pose node transform + const ElementPtr Transform = GetRequiredElement(sc, "Matrix", element); + transform = ReadMatrix(Transform); + + // get node id this pose node is for + const ElementPtr NodeId = sc->GetElement("Node3D"); + if (NodeId) { + target_id = ParseTokenAsInt64(GetRequiredToken(NodeId, 0)); + } + + print_verbose("added posenode " + itos(target_id) + " transform: " + transform); + } + virtual ~FbxPoseNode() { + } + + uint64_t GetNodeID() const { + return target_id; + } + + Transform GetBindPose() const { + return transform; + } + +private: + uint64_t target_id; + Transform transform; +}; + +/** DOM base class for FBX cameras attached to a node */ +class Camera : public NodeAttribute { +public: + Camera(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); + + virtual ~Camera(); + + fbx_simple_property(Position, Vector3, Vector3(0, 0, 0)); + fbx_simple_property(UpVector, Vector3, Vector3(0, 1, 0)); + fbx_simple_property(InterestPosition, Vector3, Vector3(0, 0, 0)); + + fbx_simple_property(AspectWidth, float, 1.0f); + fbx_simple_property(AspectHeight, float, 1.0f); + fbx_simple_property(FilmWidth, float, 1.0f); + fbx_simple_property(FilmHeight, float, 1.0f); + + fbx_simple_property(NearPlane, float, 0.1f); + fbx_simple_property(FarPlane, float, 100.0f); + + fbx_simple_property(FilmAspectRatio, float, 1.0f); + fbx_simple_property(ApertureMode, int, 0); + + fbx_simple_property(FieldOfView, float, 1.0f); + fbx_simple_property(FocalLength, float, 1.0f); +}; + +/** DOM base class for FBX null markers attached to a node */ +class Null : public NodeAttribute { +public: + Null(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); + virtual ~Null(); +}; + +/** DOM base class for FBX limb node markers attached to a node */ +class LimbNode : public NodeAttribute { +public: + LimbNode(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); + virtual ~LimbNode(); +}; + +/** DOM base class for FBX lights attached to a node */ +class Light : public NodeAttribute { +public: + Light(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); + virtual ~Light(); + + enum Type { + Type_Point, + Type_Directional, + Type_Spot, + Type_Area, + Type_Volume, + + Type_MAX // end-of-enum sentinel + }; + + enum Decay { + Decay_None, + Decay_Linear, + Decay_Quadratic, + Decay_Cubic, + + Decay_MAX // end-of-enum sentinel + }; + + fbx_simple_property(Color, Vector3, Vector3(1, 1, 1)); + fbx_simple_enum_property(LightType, Type, 0); + fbx_simple_property(CastLightOnObject, bool, false); + fbx_simple_property(DrawVolumetricLight, bool, true); + fbx_simple_property(DrawGroundProjection, bool, true); + fbx_simple_property(DrawFrontFacingVolumetricLight, bool, false); + fbx_simple_property(Intensity, float, 100.0f); + fbx_simple_property(InnerAngle, float, 0.0f); + fbx_simple_property(OuterAngle, float, 45.0f); + fbx_simple_property(Fog, int, 50); + fbx_simple_enum_property(DecayType, Decay, 2); + fbx_simple_property(DecayStart, float, 1.0f); + fbx_simple_property(FileName, std::string, ""); + + fbx_simple_property(EnableNearAttenuation, bool, false); + fbx_simple_property(NearAttenuationStart, float, 0.0f); + fbx_simple_property(NearAttenuationEnd, float, 0.0f); + fbx_simple_property(EnableFarAttenuation, bool, false); + fbx_simple_property(FarAttenuationStart, float, 0.0f); + fbx_simple_property(FarAttenuationEnd, float, 0.0f); + + fbx_simple_property(CastShadows, bool, true); + fbx_simple_property(ShadowColor, Vector3, Vector3(0, 0, 0)); + + fbx_simple_property(AreaLightShape, int, 0); + + fbx_simple_property(LeftBarnDoor, float, 20.0f); + fbx_simple_property(RightBarnDoor, float, 20.0f); + fbx_simple_property(TopBarnDoor, float, 20.0f); + fbx_simple_property(BottomBarnDoor, float, 20.0f); + fbx_simple_property(EnableBarnDoor, bool, true); +}; + +class Model; + +typedef Model *ModelPtr; +#define new_Model new Model + +/** DOM base class for FBX models (even though its semantics are more "node" than "model" */ +class Model : public Object { +public: + enum RotOrder { + RotOrder_EulerXYZ = 0, + RotOrder_EulerXZY, + RotOrder_EulerYZX, + RotOrder_EulerYXZ, + RotOrder_EulerZXY, + RotOrder_EulerZYX, + + RotOrder_SphericXYZ, + + RotOrder_MAX // end-of-enum sentinel + }; + + Model(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); + + virtual ~Model(); + + fbx_simple_property(QuaternionInterpolate, int, 0); + + fbx_simple_property(RotationOffset, Vector3, Vector3()); + fbx_simple_property(RotationPivot, Vector3, Vector3()); + fbx_simple_property(ScalingOffset, Vector3, Vector3()); + fbx_simple_property(ScalingPivot, Vector3, Vector3()); + fbx_simple_property(TranslationActive, bool, false); + fbx_simple_property(TranslationMin, Vector3, Vector3()); + fbx_simple_property(TranslationMax, Vector3, Vector3()); + + fbx_simple_property(TranslationMinX, bool, false); + fbx_simple_property(TranslationMaxX, bool, false); + fbx_simple_property(TranslationMinY, bool, false); + fbx_simple_property(TranslationMaxY, bool, false); + fbx_simple_property(TranslationMinZ, bool, false); + fbx_simple_property(TranslationMaxZ, bool, false); + + fbx_simple_enum_property(RotationOrder, RotOrder, 0); + fbx_simple_property(RotationSpaceForLimitOnly, bool, false); + fbx_simple_property(RotationStiffnessX, float, 0.0f); + fbx_simple_property(RotationStiffnessY, float, 0.0f); + fbx_simple_property(RotationStiffnessZ, float, 0.0f); + fbx_simple_property(AxisLen, float, 0.0f); + + fbx_simple_property(PreRotation, Vector3, Vector3()); + fbx_simple_property(PostRotation, Vector3, Vector3()); + fbx_simple_property(RotationActive, bool, false); + + fbx_simple_property(RotationMin, Vector3, Vector3()); + fbx_simple_property(RotationMax, Vector3, Vector3()); + + fbx_simple_property(RotationMinX, bool, false); + fbx_simple_property(RotationMaxX, bool, false); + fbx_simple_property(RotationMinY, bool, false); + fbx_simple_property(RotationMaxY, bool, false); + fbx_simple_property(RotationMinZ, bool, false); + fbx_simple_property(RotationMaxZ, bool, false); + fbx_simple_enum_property(InheritType, TransformInheritance, 0); + + fbx_simple_property(ScalingActive, bool, false); + fbx_simple_property(ScalingMin, Vector3, Vector3()); + fbx_simple_property(ScalingMax, Vector3, Vector3(1, 1, 1)); + fbx_simple_property(ScalingMinX, bool, false); + fbx_simple_property(ScalingMaxX, bool, false); + fbx_simple_property(ScalingMinY, bool, false); + fbx_simple_property(ScalingMaxY, bool, false); + fbx_simple_property(ScalingMinZ, bool, false); + fbx_simple_property(ScalingMaxZ, bool, false); + + fbx_simple_property(GeometricTranslation, Vector3, Vector3()); + fbx_simple_property(GeometricRotation, Vector3, Vector3()); + fbx_simple_property(GeometricScaling, Vector3, Vector3(1, 1, 1)); + + fbx_simple_property(MinDampRangeX, float, 0.0f); + fbx_simple_property(MinDampRangeY, float, 0.0f); + fbx_simple_property(MinDampRangeZ, float, 0.0f); + fbx_simple_property(MaxDampRangeX, float, 0.0f); + fbx_simple_property(MaxDampRangeY, float, 0.0f); + fbx_simple_property(MaxDampRangeZ, float, 0.0f); + + fbx_simple_property(MinDampStrengthX, float, 0.0f); + fbx_simple_property(MinDampStrengthY, float, 0.0f); + fbx_simple_property(MinDampStrengthZ, float, 0.0f); + fbx_simple_property(MaxDampStrengthX, float, 0.0f); + fbx_simple_property(MaxDampStrengthY, float, 0.0f); + fbx_simple_property(MaxDampStrengthZ, float, 0.0f); + + fbx_simple_property(PreferredAngleX, float, 0.0f); + fbx_simple_property(PreferredAngleY, float, 0.0f); + fbx_simple_property(PreferredAngleZ, float, 0.0f); + + fbx_simple_property(Show, bool, true); + fbx_simple_property(LODBox, bool, false); + fbx_simple_property(Freeze, bool, false); + + const std::string &Shading() const { + return shading; + } + + const std::string &Culling() const { + return culling; + } + + const PropertyTable *Props() const { + return props; + } + + /** Get material links */ + const std::vector<const Material *> &GetMaterials() const { + return materials; + } + + /** Get geometry links */ + const std::vector<const Geometry *> &GetGeometry() const { + return geometry; + } + + /** Get node attachments */ + const std::vector<const NodeAttribute *> &GetAttributes() const { + return attributes; + } + + /** convenience method to check if the node has a Null node marker */ + bool IsNull() const; + +private: + void ResolveLinks(const ElementPtr element, const Document &doc); + +private: + std::vector<const Material *> materials; + std::vector<const Geometry *> geometry; + std::vector<const NodeAttribute *> attributes; + + std::string shading; + std::string culling; + const PropertyTable *props = nullptr; +}; + +class ModelLimbNode : public Model { +public: + ModelLimbNode(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); + + virtual ~ModelLimbNode(); +}; + +/** DOM class for generic FBX textures */ +class Texture : public Object { +public: + Texture(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); + + virtual ~Texture(); + + const std::string &Type() const { + return type; + } + + const std::string &FileName() const { + return fileName; + } + + const std::string &RelativeFilename() const { + return relativeFileName; + } + + const std::string &AlphaSource() const { + return alphaSource; + } + + const Vector2 &UVTranslation() const { + return uvTrans; + } + + const Vector2 &UVScaling() const { + return uvScaling; + } + + const PropertyTable *Props() const { + return props; + } + + // return a 4-tuple + const unsigned int *Crop() const { + return crop; + } + + const Video *Media() const { + return media; + } + +private: + Vector2 uvTrans; + Vector2 uvScaling; + + std::string type; + std::string relativeFileName; + std::string fileName; + std::string alphaSource; + const PropertyTable *props = nullptr; + + unsigned int crop[4] = { 0 }; + + const Video *media = nullptr; +}; + +/** DOM class for layered FBX textures */ +class LayeredTexture : public Object { +public: + LayeredTexture(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); + virtual ~LayeredTexture(); + + // Can only be called after construction of the layered texture object due to construction flag. + void fillTexture(const Document &doc); + + enum BlendMode { + BlendMode_Translucent, + BlendMode_Additive, + BlendMode_Modulate, + BlendMode_Modulate2, + BlendMode_Over, + BlendMode_Normal, + BlendMode_Dissolve, + BlendMode_Darken, + BlendMode_ColorBurn, + BlendMode_LinearBurn, + BlendMode_DarkerColor, + BlendMode_Lighten, + BlendMode_Screen, + BlendMode_ColorDodge, + BlendMode_LinearDodge, + BlendMode_LighterColor, + BlendMode_SoftLight, + BlendMode_HardLight, + BlendMode_VividLight, + BlendMode_LinearLight, + BlendMode_PinLight, + BlendMode_HardMix, + BlendMode_Difference, + BlendMode_Exclusion, + BlendMode_Subtract, + BlendMode_Divide, + BlendMode_Hue, + BlendMode_Saturation, + BlendMode_Color, + BlendMode_Luminosity, + BlendMode_Overlay, + BlendMode_BlendModeCount + }; + + const Texture *getTexture(int index = 0) const { + return textures[index]; + } + int textureCount() const { + return static_cast<int>(textures.size()); + } + BlendMode GetBlendMode() const { + return blendMode; + } + float Alpha() { + return alpha; + } + +private: + std::vector<const Texture *> textures; + BlendMode blendMode; + float alpha; +}; + +typedef std::map<std::string, const Texture *> TextureMap; +typedef std::map<std::string, const LayeredTexture *> LayeredTextureMap; + +/** DOM class for generic FBX videos */ +class Video : public Object { +public: + Video(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); + + virtual ~Video(); + + const std::string &Type() const { + return type; + } + + bool IsEmbedded() const { + return contentLength > 0; + } + + const std::string &FileName() const { + return fileName; + } + + const std::string &RelativeFilename() const { + return relativeFileName; + } + + const PropertyTable *Props() const { + return props; + } + + const uint8_t *Content() const { + return content; + } + + uint64_t ContentLength() const { + return contentLength; + } + + uint8_t *RelinquishContent() { + uint8_t *ptr = content; + content = 0; + return ptr; + } + + bool operator==(const Video &other) const { + return ( + type == other.type && relativeFileName == other.relativeFileName && fileName == other.fileName); + } + + bool operator<(const Video &other) const { + return std::tie(type, relativeFileName, fileName) < std::tie(other.type, other.relativeFileName, other.fileName); + } + +private: + std::string type; + std::string relativeFileName; + std::string fileName; + const PropertyTable *props = nullptr; + + uint64_t contentLength = 0; + uint8_t *content = nullptr; +}; + +/** DOM class for generic FBX materials */ +class Material : public Object { +public: + Material(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); + + virtual ~Material(); + + const std::string &GetShadingModel() const { + return shading; + } + + bool IsMultilayer() const { + return multilayer; + } + + const PropertyTable *Props() const { + return props; + } + + const TextureMap &Textures() const { + return textures; + } + + const LayeredTextureMap &LayeredTextures() const { + return layeredTextures; + } + +private: + std::string shading; + bool multilayer; + const PropertyTable *props; + + TextureMap textures; + LayeredTextureMap layeredTextures; +}; + +// signed int keys (this can happen!) +typedef std::vector<int64_t> KeyTimeList; +typedef std::vector<float> KeyValueList; + +/** Represents a FBX animation curve (i.e. a 1-dimensional set of keyframes and values therefor) */ +class AnimationCurve : public Object { +public: + AnimationCurve(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc); + virtual ~AnimationCurve(); + + /** get list of keyframe positions (time). + * Invariant: |GetKeys()| > 0 */ + const KeyTimeList &GetKeys() const { + return keys; + } + + /** get list of keyframe values. + * Invariant: |GetKeys()| == |GetValues()| && |GetKeys()| > 0*/ + const KeyValueList &GetValues() const { + return values; + } + + const std::map<int64_t, float> &GetValueTimeTrack() const { + return keyvalues; + } + + const std::vector<float> &GetAttributes() const { + return attributes; + } + + const std::vector<unsigned int> &GetFlags() const { + return flags; + } + +private: + KeyTimeList keys; + KeyValueList values; + std::vector<float> attributes; + std::map<int64_t, float> keyvalues; + std::vector<unsigned int> flags; +}; + +/* Typedef for pointers for the animation handler */ +typedef std::shared_ptr<AnimationCurve> AnimationCurvePtr; +typedef std::weak_ptr<AnimationCurve> AnimationCurveWeakPtr; +typedef std::map<std::string, const AnimationCurve *> AnimationMap; + +/* Animation Curve node ptr */ +typedef std::shared_ptr<AnimationCurveNode> AnimationCurveNodePtr; +typedef std::weak_ptr<AnimationCurveNode> AnimationCurveNodeWeakPtr; + +/** Represents a FBX animation curve (i.e. a mapping from single animation curves to nodes) */ +class AnimationCurveNode : public Object { +public: + /* the optional white list specifies a list of property names for which the caller + wants animations for. If the curve node does not match one of these, std::range_error + will be thrown. */ + AnimationCurveNode(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc, + const char *const *target_prop_whitelist = nullptr, size_t whitelist_size = 0); + + virtual ~AnimationCurveNode(); + + const PropertyTable *Props() const { + return props; + } + + const AnimationMap &Curves() const; + + /** Object the curve is assigned to, this can be NULL if the + * target object has no DOM representation or could not + * be read for other reasons.*/ + Object *Target() const { + return target; + } + + Model *TargetAsModel() const { + return dynamic_cast<Model *>(target); + } + + NodeAttribute *TargetAsNodeAttribute() const { + return dynamic_cast<NodeAttribute *>(target); + } + + /** Property of Target() that is being animated*/ + const std::string &TargetProperty() const { + return prop; + } + +private: + Object *target = nullptr; + const PropertyTable *props; + mutable AnimationMap curves; + std::string prop; + const Document &doc; +}; + +typedef std::vector<const AnimationCurveNode *> AnimationCurveNodeList; + +typedef std::shared_ptr<AnimationLayer> AnimationLayerPtr; +typedef std::weak_ptr<AnimationLayer> AnimationLayerWeakPtr; +typedef std::vector<const AnimationLayer *> AnimationLayerList; + +/** Represents a FBX animation layer (i.e. a list of node animations) */ +class AnimationLayer : public Object { +public: + AnimationLayer(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc); + virtual ~AnimationLayer(); + + const PropertyTable *Props() const { + //ai_assert(props.get()); + return props; + } + + /* the optional white list specifies a list of property names for which the caller + wants animations for. Curves not matching this list will not be added to the + animation layer. */ + const AnimationCurveNodeList Nodes(const char *const *target_prop_whitelist = nullptr, size_t whitelist_size = 0) const; + +private: + const PropertyTable *props; + const Document &doc; +}; + +/** Represents a FBX animation stack (i.e. a list of animation layers) */ +class AnimationStack : public Object { +public: + AnimationStack(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc); + virtual ~AnimationStack(); + + fbx_simple_property(LocalStart, int64_t, 0L); + fbx_simple_property(LocalStop, int64_t, 0L); + fbx_simple_property(ReferenceStart, int64_t, 0L); + fbx_simple_property(ReferenceStop, int64_t, 0L); + + const PropertyTable *Props() const { + return props; + } + + const AnimationLayerList &Layers() const { + return layers; + } + +private: + const PropertyTable *props = nullptr; + AnimationLayerList layers; +}; + +/** DOM class for deformers */ +class Deformer : public Object { +public: + Deformer(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); + virtual ~Deformer(); + + const PropertyTable *Props() const { + //ai_assert(props.get()); + return props; + } + +private: + const PropertyTable *props; +}; + +/** Constraints are from Maya they can help us with BoneAttachments :) **/ +class Constraint : public Object { +public: + Constraint(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); + virtual ~Constraint(); + +private: + const PropertyTable *props; +}; + +typedef std::vector<float> WeightArray; +typedef std::vector<unsigned int> WeightIndexArray; + +/** DOM class for BlendShapeChannel deformers */ +class BlendShapeChannel : public Deformer { +public: + BlendShapeChannel(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); + + virtual ~BlendShapeChannel(); + + float DeformPercent() const { + return percent; + } + + const WeightArray &GetFullWeights() const { + return fullWeights; + } + + const std::vector<const ShapeGeometry *> &GetShapeGeometries() const { + return shapeGeometries; + } + +private: + float percent; + WeightArray fullWeights; + std::vector<const ShapeGeometry *> shapeGeometries; +}; + +/** DOM class for BlendShape deformers */ +class BlendShape : public Deformer { +public: + BlendShape(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); + + virtual ~BlendShape(); + + const std::vector<const BlendShapeChannel *> &BlendShapeChannels() const { + return blendShapeChannels; + } + +private: + std::vector<const BlendShapeChannel *> blendShapeChannels; +}; + +/** DOM class for skin deformer clusters (aka sub-deformers) */ +class Cluster : public Deformer { +public: + Cluster(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); + + virtual ~Cluster(); + + /** get the list of deformer weights associated with this cluster. + * Use #GetIndices() to get the associated vertices. Both arrays + * have the same size (and may also be empty). */ + const std::vector<float> &GetWeights() const { + return weights; + } + + /** get indices into the vertex data of the geometry associated + * with this cluster. Use #GetWeights() to get the associated weights. + * Both arrays have the same size (and may also be empty). */ + const std::vector<unsigned int> &GetIndices() const { + return indices; + } + + /** */ + const Transform &GetTransform() const { + return transform; + } + + const Transform &TransformLink() const { + return transformLink; + } + + const Model *TargetNode() const { + return node; + } + + const Transform &TransformAssociateModel() const { + return transformAssociateModel; + } + + bool TransformAssociateModelValid() const { + return valid_transformAssociateModel; + } + + // property is not in the fbx file + // if the cluster has an associate model + // we then have an additive type + enum SkinLinkMode { + SkinLinkMode_Normalized = 0, + SkinLinkMode_Additive = 1 + }; + + SkinLinkMode GetLinkMode() { + return link_mode; + } + +private: + std::vector<float> weights; + std::vector<unsigned int> indices; + + Transform transform; + Transform transformLink; + Transform transformAssociateModel; + SkinLinkMode link_mode; + bool valid_transformAssociateModel; + const Model *node = nullptr; +}; + +/** DOM class for skin deformers */ +class Skin : public Deformer { +public: + Skin(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name); + + virtual ~Skin(); + + float DeformAccuracy() const { + return accuracy; + } + + const std::vector<const Cluster *> &Clusters() const { + return clusters; + } + + enum SkinType { + Skin_Rigid = 0, + Skin_Linear, + Skin_DualQuaternion, + Skin_Blend + }; + + const SkinType &GetSkinType() const { + return skinType; + } + +private: + float accuracy; + SkinType skinType; + std::vector<const Cluster *> clusters; +}; + +/** Represents a link between two FBX objects. */ +class Connection { +public: + Connection(uint64_t insertionOrder, uint64_t src, uint64_t dest, const std::string &prop, const Document &doc); + ~Connection(); + + // note: a connection ensures that the source and dest objects exist, but + // not that they have DOM representations, so the return value of one of + // these functions can still be NULL. + Object *SourceObject() const; + Object *DestinationObject() const; + + // these, however, are always guaranteed to be valid + LazyObject *LazySourceObject() const; + LazyObject *LazyDestinationObject() const; + + /** return the name of the property the connection is attached to. + * this is an empty string for object to object (OO) connections. */ + const std::string &PropertyName() const { + return prop; + } + + uint64_t InsertionOrder() const { + return insertionOrder; + } + + int CompareTo(const Connection *c) const { + //ai_assert(nullptr != c); + + // note: can't subtract because this would overflow uint64_t + if (InsertionOrder() > c->InsertionOrder()) { + return 1; + } else if (InsertionOrder() < c->InsertionOrder()) { + return -1; + } + return 0; + } + + bool Compare(const Connection *c) const { + //ai_assert(nullptr != c); + + return InsertionOrder() < c->InsertionOrder(); + } + +public: + uint64_t insertionOrder; + const std::string prop; + + uint64_t src, dest; + const Document &doc; +}; + +// XXX again, unique_ptr would be useful. shared_ptr is too +// bloated since the objects have a well-defined single owner +// during their entire lifetime (Document). FBX files have +// up to many thousands of objects (most of which we never use), +// so the memory overhead for them should be kept at a minimum. +typedef std::map<uint64_t, LazyObject *> ObjectMap; +typedef std::map<std::string, const PropertyTable *> PropertyTemplateMap; +typedef std::multimap<uint64_t, const Connection *> ConnectionMap; + +/** DOM class for global document settings, a single instance per document can + * be accessed via Document.Globals(). */ +class FileGlobalSettings { +public: + FileGlobalSettings(const Document &doc, const PropertyTable *props); + + ~FileGlobalSettings(); + + const PropertyTable *Props() const { + return props; + } + + const Document &GetDocument() const { + return doc; + } + + fbx_simple_property(UpAxis, int, 1); + fbx_simple_property(UpAxisSign, int, 1); + fbx_simple_property(FrontAxis, int, 2); + fbx_simple_property(FrontAxisSign, int, 1); + fbx_simple_property(CoordAxis, int, 0); + fbx_simple_property(CoordAxisSign, int, 1); + fbx_simple_property(OriginalUpAxis, int, 0); + fbx_simple_property(OriginalUpAxisSign, int, 1); + fbx_simple_property(UnitScaleFactor, float, 1); + fbx_simple_property(OriginalUnitScaleFactor, float, 1); + fbx_simple_property(AmbientColor, Vector3, Vector3(0, 0, 0)); + fbx_simple_property(DefaultCamera, std::string, ""); + + enum FrameRate { + FrameRate_DEFAULT = 0, + FrameRate_120 = 1, + FrameRate_100 = 2, + FrameRate_60 = 3, + FrameRate_50 = 4, + FrameRate_48 = 5, + FrameRate_30 = 6, + FrameRate_30_DROP = 7, + FrameRate_NTSC_DROP_FRAME = 8, + FrameRate_NTSC_FULL_FRAME = 9, + FrameRate_PAL = 10, + FrameRate_CINEMA = 11, + FrameRate_1000 = 12, + FrameRate_CINEMA_ND = 13, + FrameRate_CUSTOM = 14, + + FrameRate_MAX // end-of-enum sentinel + }; + + fbx_simple_enum_property(TimeMode, FrameRate, FrameRate_DEFAULT); + fbx_simple_property(TimeSpanStart, uint64_t, 0L); + fbx_simple_property(TimeSpanStop, uint64_t, 0L); + fbx_simple_property(CustomFrameRate, float, -1.0f); + +private: + const PropertyTable *props = nullptr; + const Document &doc; +}; + +/** DOM root for a FBX file */ +class Document { +public: + Document(const Parser &parser, const ImportSettings &settings); + + ~Document(); + + LazyObject *GetObject(uint64_t id) const; + + bool IsSafeToImport() const { + return SafeToImport; + } + + bool IsBinary() const { + return parser.IsBinary(); + } + + unsigned int FBXVersion() const { + return fbxVersion; + } + + const std::string &Creator() const { + return creator; + } + + // elements (in this order): Year, Month, Day, Hour, Second, Millisecond + const unsigned int *CreationTimeStamp() const { + return creationTimeStamp; + } + + const FileGlobalSettings *GlobalSettingsPtr() const { + return globals.get(); + } + + const PropertyTable *GetMetadataProperties() const { + return metadata_properties; + } + + const PropertyTemplateMap &Templates() const { + return templates; + } + + const ObjectMap &Objects() const { + return objects; + } + + const ImportSettings &Settings() const { + return settings; + } + + const ConnectionMap &ConnectionsBySource() const { + return src_connections; + } + + const ConnectionMap &ConnectionsByDestination() const { + return dest_connections; + } + + // note: the implicit rule in all DOM classes is to always resolve + // from destination to source (since the FBX object hierarchy is, + // with very few exceptions, a DAG, this avoids cycles). In all + // cases that may involve back-facing edges in the object graph, + // use LazyObject::IsBeingConstructed() to check. + + std::vector<const Connection *> GetConnectionsBySourceSequenced(uint64_t source) const; + std::vector<const Connection *> GetConnectionsByDestinationSequenced(uint64_t dest) const; + + std::vector<const Connection *> GetConnectionsBySourceSequenced(uint64_t source, const char *classname) const; + std::vector<const Connection *> GetConnectionsByDestinationSequenced(uint64_t dest, const char *classname) const; + + std::vector<const Connection *> GetConnectionsBySourceSequenced(uint64_t source, + const char *const *classnames, size_t count) const; + std::vector<const Connection *> GetConnectionsByDestinationSequenced(uint64_t dest, + const char *const *classnames, + size_t count) const; + + const std::vector<const AnimationStack *> &AnimationStacks() const; + const std::vector<uint64_t> &GetAnimationStackIDs() const { + return animationStacks; + } + + const std::vector<uint64_t> &GetConstraintStackIDs() const { + return constraints; + } + + const std::vector<uint64_t> &GetBindPoseIDs() const { + return bind_poses; + }; + + const std::vector<uint64_t> &GetMaterialIDs() const { + return materials; + }; + + const std::vector<uint64_t> &GetSkinIDs() const { + return skins; + } + +private: + std::vector<const Connection *> GetConnectionsSequenced(uint64_t id, const ConnectionMap &) const; + std::vector<const Connection *> GetConnectionsSequenced(uint64_t id, bool is_src, + const ConnectionMap &, + const char *const *classnames, + size_t count) const; + bool ReadHeader(); + void ReadObjects(); + void ReadPropertyTemplates(); + void ReadConnections(); + void ReadGlobalSettings(); + +private: + const ImportSettings &settings; + + ObjectMap objects; + const Parser &parser; + bool SafeToImport = false; + + PropertyTemplateMap templates; + ConnectionMap src_connections; + ConnectionMap dest_connections; + + unsigned int fbxVersion = 0; + std::string creator; + unsigned int creationTimeStamp[7] = { 0 }; + + std::vector<uint64_t> animationStacks; + std::vector<uint64_t> bind_poses; + // constraints aren't in the tree / at least they are not easy to access. + std::vector<uint64_t> constraints; + std::vector<uint64_t> materials; + std::vector<uint64_t> skins; + mutable std::vector<const AnimationStack *> animationStacksResolved; + PropertyTable *metadata_properties = nullptr; + std::shared_ptr<FileGlobalSettings> globals = nullptr; +}; +} // namespace FBXDocParser + +namespace std { +template <> +struct hash<const FBXDocParser::Video> { + std::size_t operator()(const FBXDocParser::Video &video) const { + using std::hash; + using std::size_t; + using std::string; + + size_t res = 17; + res = res * 31 + hash<string>()(video.Name()); + res = res * 31 + hash<string>()(video.RelativeFilename()); + res = res * 31 + hash<string>()(video.Type()); + + return res; + } +}; +} // namespace std + +#endif // FBX_DOCUMENT_H diff --git a/modules/fbx/fbx_parser/FBXDocumentUtil.cpp b/modules/fbx/fbx_parser/FBXDocumentUtil.cpp new file mode 100644 index 0000000000..835b66ab23 --- /dev/null +++ b/modules/fbx/fbx_parser/FBXDocumentUtil.cpp @@ -0,0 +1,172 @@ +/*************************************************************************/ +/* FBXDocumentUtil.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXDocumentUtil.cpp + * @brief Implementation of the FBX DOM utility functions declared in FBXDocumentUtil.h + */ + +#include "FBXDocumentUtil.h" +#include "FBXDocument.h" +#include "FBXParser.h" +#include "FBXProperties.h" +#include "FBXUtil.h" +#include "core/string/print_string.h" + +namespace FBXDocParser { +namespace Util { + +void DOMError(const std::string &message) { + print_error("[FBX-DOM]" + String(message.c_str())); +} + +void DOMError(const std::string &message, const Token *token) { + print_error("[FBX-DOM]" + String(message.c_str()) + ";" + String(token->StringContents().c_str())); +} + +void DOMError(const std::string &message, const std::shared_ptr<Token> token) { + print_error("[FBX-DOM]" + String(message.c_str()) + ";" + String(token->StringContents().c_str())); +} + +void DOMError(const std::string &message, const Element *element /*= NULL*/) { + if (element) { + DOMError(message, element->KeyToken()); + } + print_error("[FBX-DOM] " + String(message.c_str())); +} + +void DOMError(const std::string &message, const std::shared_ptr<Element> element /*= NULL*/) { + if (element) { + DOMError(message, element->KeyToken()); + } + print_error("[FBX-DOM] " + String(message.c_str())); +} + +void DOMWarning(const std::string &message) { + print_verbose("[FBX-DOM] warning:" + String(message.c_str())); +} + +void DOMWarning(const std::string &message, const Token *token) { + print_verbose("[FBX-DOM] warning:" + String(message.c_str()) + ";" + String(token->StringContents().c_str())); +} + +void DOMWarning(const std::string &message, const Element *element /*= NULL*/) { + if (element) { + DOMWarning(message, element->KeyToken()); + return; + } + print_verbose("[FBX-DOM] warning:" + String(message.c_str())); +} + +void DOMWarning(const std::string &message, const std::shared_ptr<Token> token) { + print_verbose("[FBX-DOM] warning:" + String(message.c_str()) + ";" + String(token->StringContents().c_str())); +} + +void DOMWarning(const std::string &message, const std::shared_ptr<Element> element /*= NULL*/) { + if (element) { + DOMWarning(message, element->KeyToken()); + return; + } + print_verbose("[FBX-DOM] warning:" + String(message.c_str())); +} + +// ------------------------------------------------------------------------------------------------ +// fetch a property table and the corresponding property template +const PropertyTable *GetPropertyTable(const Document &doc, + const std::string &templateName, + const ElementPtr element, + const ScopePtr sc, + bool no_warn /*= false*/) { + // todo: make this an abstraction + const ElementPtr Properties70 = sc->GetElement("Properties70"); + const PropertyTable *templateProps = static_cast<const PropertyTable *>(nullptr); + + if (templateName.length()) { + PropertyTemplateMap::const_iterator it = doc.Templates().find(templateName); + if (it != doc.Templates().end()) { + templateProps = (*it).second; + } + } + + if (!Properties70 || !Properties70->Compound()) { + if (!no_warn) { + DOMWarning("property table (Properties70) not found", element); + } + if (templateProps) { + return templateProps; + } else { + return new const PropertyTable(); + } + } + + return new PropertyTable(Properties70, templateProps); +} +} // namespace Util +} // namespace FBXDocParser diff --git a/modules/fbx/fbx_parser/FBXDocumentUtil.h b/modules/fbx/fbx_parser/FBXDocumentUtil.h new file mode 100644 index 0000000000..daa9de4a33 --- /dev/null +++ b/modules/fbx/fbx_parser/FBXDocumentUtil.h @@ -0,0 +1,141 @@ +/*************************************************************************/ +/* FBXDocumentUtil.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXDocumentUtil.h + * @brief FBX internal utilities used by the DOM reading code + */ +#ifndef FBX_DOCUMENT_UTIL_H +#define FBX_DOCUMENT_UTIL_H + +#include "FBXDocument.h" +#include <memory> +#include <string> + +struct Token; +struct Element; + +namespace FBXDocParser { +namespace Util { + +// Parser errors +void DOMError(const std::string &message); +void DOMError(const std::string &message, const Token *token); +void DOMError(const std::string &message, const Element *element); +void DOMError(const std::string &message, const std::shared_ptr<Element> element); +void DOMError(const std::string &message, const std::shared_ptr<Token> token); + +// Parser warnings +void DOMWarning(const std::string &message); +void DOMWarning(const std::string &message, const Token *token); +void DOMWarning(const std::string &message, const Element *element); +void DOMWarning(const std::string &message, const std::shared_ptr<Token> token); +void DOMWarning(const std::string &message, const std::shared_ptr<Element> element); + +// fetch a property table and the corresponding property template +const PropertyTable *GetPropertyTable(const Document &doc, + const std::string &templateName, + const ElementPtr element, + const ScopePtr sc, + bool no_warn = false); + +// ------------------------------------------------------------------------------------------------ +template <typename T> +const T *ProcessSimpleConnection(const Connection &con, + bool is_object_property_conn, + const char *name, + const ElementPtr element, + const char **propNameOut = nullptr) { + if (is_object_property_conn && !con.PropertyName().length()) { + DOMWarning("expected incoming " + std::string(name) + + " link to be an object-object connection, ignoring", + element); + return nullptr; + } else if (!is_object_property_conn && con.PropertyName().length()) { + DOMWarning("expected incoming " + std::string(name) + + " link to be an object-property connection, ignoring", + element); + return nullptr; + } + + if (is_object_property_conn && propNameOut) { + // note: this is ok, the return value of PropertyValue() is guaranteed to + // remain valid and unchanged as long as the document exists. + *propNameOut = con.PropertyName().c_str(); + } + + // Cast Object to AnimationPlayer for example using safe functions, which return nullptr etc + Object *ob = con.SourceObject(); + ERR_FAIL_COND_V_MSG(!ob, nullptr, "Failed to load object from SourceObject ptr"); + return dynamic_cast<const T *>(ob); +} +} // namespace Util +} // namespace FBXDocParser + +#endif // FBX_DOCUMENT_UTIL_H diff --git a/modules/fbx/fbx_parser/FBXImportSettings.h b/modules/fbx/fbx_parser/FBXImportSettings.h new file mode 100644 index 0000000000..97ce496eaf --- /dev/null +++ b/modules/fbx/fbx_parser/FBXImportSettings.h @@ -0,0 +1,173 @@ +/*************************************************************************/ +/* FBXImportSettings.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXImportSettings.h + * @brief FBX importer runtime configuration + */ +#ifndef FBX_IMPORT_SETTINGS_H +#define FBX_IMPORT_SETTINGS_H + +namespace FBXDocParser { + +/** FBX import settings, parts of which are publicly accessible via their corresponding AI_CONFIG constants */ +struct ImportSettings { + ImportSettings() : + strictMode(true), readAllLayers(true), readAllMaterials(true), readMaterials(true), readTextures(true), readCameras(true), readLights(true), readAnimations(true), readWeights(true), preservePivots(true), optimizeEmptyAnimationCurves(true), useLegacyEmbeddedTextureNaming(false), removeEmptyBones(true), convertToMeters(false) { + // empty + } + + /** enable strict mode: + * - only accept fbx 2012, 2013 files + * - on the slightest error, give up. + * + * Basically, strict mode means that the fbx file will actually + * be validated. Strict mode is off by default. */ + bool strictMode; + + /** specifies whether all geometry layers are read and scanned for + * usable data channels. The FBX spec indicates that many readers + * will only read the first channel and that this is in some way + * the recommended way- in reality, however, it happens a lot that + * vertex data is spread among multiple layers. The default + * value for this option is true.*/ + bool readAllLayers; + + /** specifies whether all materials are read, or only those that + * are referenced by at least one mesh. Reading all materials + * may make FBX reading a lot slower since all objects + * need to be processed . + * This bit is ignored unless readMaterials=true*/ + bool readAllMaterials; + + /** import materials (true) or skip them and assign a default + * material. The default value is true.*/ + bool readMaterials; + + /** import embedded textures? Default value is true.*/ + bool readTextures; + + /** import cameras? Default value is true.*/ + bool readCameras; + + /** import light sources? Default value is true.*/ + bool readLights; + + /** import animations (i.e. animation curves, the node + * skeleton is always imported). Default value is true. */ + bool readAnimations; + + /** read bones (vertex weights and deform info). + * Default value is true. */ + bool readWeights; + + /** preserve transformation pivots and offsets. Since these can + * not directly be represented in assimp, additional dummy + * nodes will be generated. Note that settings this to false + * can make animation import a lot slower. The default value + * is true. + * + * The naming scheme for the generated nodes is: + * <OriginalName>_$AssimpFbx$_<TransformName> + * + * where <TransformName> is one of + * RotationPivot + * RotationOffset + * PreRotation + * PostRotation + * ScalingPivot + * ScalingOffset + * Translation + * Scaling + * Rotation + **/ + bool preservePivots; + + /** do not import animation curves that specify a constant + * values matching the corresponding node transformation. + * The default value is true. */ + bool optimizeEmptyAnimationCurves; + + /** use legacy naming for embedded textures eg: (*0, *1, *2) + */ + bool useLegacyEmbeddedTextureNaming; + + /** Empty bones shall be removed + */ + bool removeEmptyBones; + + /** Set to true to perform a conversion from cm to meter after the import + */ + bool convertToMeters; +}; +} // namespace FBXDocParser + +#endif // FBX_IMPORT_SETTINGS_H diff --git a/modules/fbx/fbx_parser/FBXMaterial.cpp b/modules/fbx/fbx_parser/FBXMaterial.cpp new file mode 100644 index 0000000000..9970a2b0b1 --- /dev/null +++ b/modules/fbx/fbx_parser/FBXMaterial.cpp @@ -0,0 +1,407 @@ +/*************************************************************************/ +/* FBXMaterial.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2020, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXMaterial.cpp + * @brief Assimp::FBX::Material and Assimp::FBX::Texture implementation + */ + +#include "ByteSwapper.h" +#include "FBXDocument.h" +#include "FBXDocumentUtil.h" +#include "FBXImportSettings.h" +#include "FBXParser.h" +#include "FBXProperties.h" + +#include "FBXUtil.h" +#include <algorithm> // std::transform + +namespace FBXDocParser { + +using namespace Util; + +// ------------------------------------------------------------------------------------------------ +Material::Material(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : + Object(id, element, name) { + const ScopePtr sc = GetRequiredScope(element); + + const ElementPtr ShadingModel = sc->GetElement("ShadingModel"); + const ElementPtr MultiLayer = sc->GetElement("MultiLayer"); + + if (MultiLayer) { + multilayer = !!ParseTokenAsInt(GetRequiredToken(MultiLayer, 0)); + } + + if (ShadingModel) { + shading = ParseTokenAsString(GetRequiredToken(ShadingModel, 0)); + } else { + DOMWarning("shading mode not specified, assuming phong", element); + shading = "phong"; + } + + std::string templateName; + + if (shading == "phong") { + templateName = "Material.Phong"; + } else if (shading == "lambert") { + templateName = "Material.Lambert"; + } else if (shading == "unknown") { + templateName = "Material.StingRay"; + } else { + DOMWarning("shading mode not recognized: " + shading, element); + } + + props = GetPropertyTable(doc, templateName, element, sc); + + // resolve texture links + const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID()); + for (const Connection *con : conns) { + // texture link to properties, not objects + if (!con->PropertyName().length()) { + continue; + } + + Object *ob = con->SourceObject(); + if (!ob) { + DOMWarning("failed to read source object for texture link, ignoring", element); + continue; + } + + const Texture *tex = dynamic_cast<const Texture *>(ob); + if (!tex) { + LayeredTexture *layeredTexture = dynamic_cast<LayeredTexture *>(ob); + + if (!layeredTexture) { + DOMWarning("source object for texture link is not a texture or layered texture, ignoring", element); + continue; + } + + const std::string &prop = con->PropertyName(); + if (layeredTextures.find(prop) != layeredTextures.end()) { + DOMWarning("duplicate layered texture link: " + prop, element); + } + + layeredTextures[prop] = layeredTexture; + layeredTexture->fillTexture(doc); + } else { + const std::string &prop = con->PropertyName(); + if (textures.find(prop) != textures.end()) { + DOMWarning("duplicate texture link: " + prop, element); + } + + textures[prop] = tex; + } + } +} + +// ------------------------------------------------------------------------------------------------ +Material::~Material() { + if (props != nullptr) { + delete props; + props = nullptr; + } +} + +// ------------------------------------------------------------------------------------------------ +Texture::Texture(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : + Object(id, element, name), uvScaling(1.0f, 1.0f), media(nullptr) { + const ScopePtr sc = GetRequiredScope(element); + + const ElementPtr Type = sc->GetElement("Type"); + const ElementPtr FileName = sc->GetElement("FileName"); + const ElementPtr RelativeFilename = sc->GetElement("RelativeFilename"); + const ElementPtr ModelUVTranslation = sc->GetElement("ModelUVTranslation"); + const ElementPtr ModelUVScaling = sc->GetElement("ModelUVScaling"); + const ElementPtr Texture_Alpha_Source = sc->GetElement("Texture_Alpha_Source"); + const ElementPtr Cropping = sc->GetElement("Cropping"); + + if (Type) { + type = ParseTokenAsString(GetRequiredToken(Type, 0)); + } + + if (FileName) { + fileName = ParseTokenAsString(GetRequiredToken(FileName, 0)); + } + + if (RelativeFilename) { + relativeFileName = ParseTokenAsString(GetRequiredToken(RelativeFilename, 0)); + } + + if (ModelUVTranslation) { + uvTrans = Vector2(ParseTokenAsFloat(GetRequiredToken(ModelUVTranslation, 0)), + ParseTokenAsFloat(GetRequiredToken(ModelUVTranslation, 1))); + } + + if (ModelUVScaling) { + uvScaling = Vector2(ParseTokenAsFloat(GetRequiredToken(ModelUVScaling, 0)), + ParseTokenAsFloat(GetRequiredToken(ModelUVScaling, 1))); + } + + if (Cropping) { + crop[0] = ParseTokenAsInt(GetRequiredToken(Cropping, 0)); + crop[1] = ParseTokenAsInt(GetRequiredToken(Cropping, 1)); + crop[2] = ParseTokenAsInt(GetRequiredToken(Cropping, 2)); + crop[3] = ParseTokenAsInt(GetRequiredToken(Cropping, 3)); + } else { + // vc8 doesn't support the crop() syntax in initialization lists + // (and vc9 WARNS about the new (i.e. compliant) behaviour). + crop[0] = crop[1] = crop[2] = crop[3] = 0; + } + + if (Texture_Alpha_Source) { + alphaSource = ParseTokenAsString(GetRequiredToken(Texture_Alpha_Source, 0)); + } + + props = GetPropertyTable(doc, "Texture.FbxFileTexture", element, sc); + + // 3DS Max and FBX SDK use "Scaling" and "Translation" instead of "ModelUVScaling" and "ModelUVTranslation". Use these properties if available. + bool ok; + const Vector3 &scaling = PropertyGet<Vector3>(props, "Scaling", ok); + if (ok) { + uvScaling.x = scaling.x; + uvScaling.y = scaling.y; + } + + const Vector3 &trans = PropertyGet<Vector3>(props, "Translation", ok); + if (ok) { + uvTrans.x = trans.x; + uvTrans.y = trans.y; + } + + // resolve video links + if (doc.Settings().readTextures) { + const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID()); + for (const Connection *con : conns) { + const Object *const ob = con->SourceObject(); + if (!ob) { + DOMWarning("failed to read source object for texture link, ignoring", element); + continue; + } + + const Video *const video = dynamic_cast<const Video *>(ob); + if (video) { + media = video; + } + } + } +} + +Texture::~Texture() { + if (props != nullptr) { + delete props; + props = nullptr; + } +} + +LayeredTexture::LayeredTexture(uint64_t id, const ElementPtr element, const Document & /*doc*/, const std::string &name) : + Object(id, element, name), blendMode(BlendMode_Modulate), alpha(1) { + const ScopePtr sc = GetRequiredScope(element); + + ElementPtr BlendModes = sc->GetElement("BlendModes"); + ElementPtr Alphas = sc->GetElement("Alphas"); + + if (BlendModes != 0) { + blendMode = (BlendMode)ParseTokenAsInt(GetRequiredToken(BlendModes, 0)); + } + if (Alphas != 0) { + alpha = ParseTokenAsFloat(GetRequiredToken(Alphas, 0)); + } +} + +LayeredTexture::~LayeredTexture() { +} + +void LayeredTexture::fillTexture(const Document &doc) { + const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID()); + for (size_t i = 0; i < conns.size(); ++i) { + const Connection *con = conns.at(i); + + const Object *const ob = con->SourceObject(); + if (!ob) { + DOMWarning("failed to read source object for texture link, ignoring", element); + continue; + } + + const Texture *const tex = dynamic_cast<const Texture *>(ob); + + textures.push_back(tex); + } +} + +// ------------------------------------------------------------------------------------------------ +Video::Video(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : + Object(id, element, name), contentLength(0), content(0) { + const ScopePtr sc = GetRequiredScope(element); + + const ElementPtr Type = sc->GetElement("Type"); + // File Version 7500 Crashes if this is not checked fully. + // As of writing this comment 7700 exists, in August 2020 + ElementPtr FileName = nullptr; + if (HasElement(sc, "Filename")) { + FileName = (ElementPtr)sc->GetElement("Filename"); + } else if (HasElement(sc, "FileName")) { + FileName = (ElementPtr)sc->GetElement("FileName"); + } else { + print_error("file has invalid video material returning..."); + return; + } + const ElementPtr RelativeFilename = sc->GetElement("RelativeFilename"); + const ElementPtr Content = sc->GetElement("Content"); + + if (Type) { + type = ParseTokenAsString(GetRequiredToken(Type, 0)); + } + + if (FileName) { + fileName = ParseTokenAsString(GetRequiredToken(FileName, 0)); + } + + if (RelativeFilename) { + relativeFileName = ParseTokenAsString(GetRequiredToken(RelativeFilename, 0)); + } + + if (Content && !Content->Tokens().empty()) { + //this field is omitted when the embedded texture is already loaded, let's ignore if it's not found + try { + const Token *token = GetRequiredToken(Content, 0); + const char *data = token->begin(); + if (!token->IsBinary()) { + if (*data != '"') { + DOMError("embedded content is not surrounded by quotation marks", element); + } else { + size_t targetLength = 0; + auto numTokens = Content->Tokens().size(); + // First time compute size (it could be large like 64Gb and it is good to allocate it once) + for (uint32_t tokenIdx = 0; tokenIdx < numTokens; ++tokenIdx) { + const Token *dataToken = GetRequiredToken(Content, tokenIdx); + size_t tokenLength = dataToken->end() - dataToken->begin() - 2; // ignore double quotes + const char *base64data = dataToken->begin() + 1; + const size_t outLength = Util::ComputeDecodedSizeBase64(base64data, tokenLength); + if (outLength == 0) { + DOMError("Corrupted embedded content found", element); + } + targetLength += outLength; + } + if (targetLength == 0) { + DOMError("Corrupted embedded content found", element); + } else { + content = new uint8_t[targetLength]; + contentLength = static_cast<uint64_t>(targetLength); + size_t dst_offset = 0; + for (uint32_t tokenIdx = 0; tokenIdx < numTokens; ++tokenIdx) { + const Token *dataToken = GetRequiredToken(Content, tokenIdx); + ERR_FAIL_COND(!dataToken); + size_t tokenLength = dataToken->end() - dataToken->begin() - 2; // ignore double quotes + const char *base64data = dataToken->begin() + 1; + dst_offset += Util::DecodeBase64(base64data, tokenLength, content + dst_offset, targetLength - dst_offset); + } + if (targetLength != dst_offset) { + delete[] content; + contentLength = 0; + DOMError("Corrupted embedded content found", element); + } + } + } + } else if (static_cast<size_t>(token->end() - data) < 5) { + DOMError("binary data array is too short, need five (5) bytes for type signature and element count", element); + } else if (*data != 'R') { + DOMWarning("video content is not raw binary data, ignoring", element); + } else { + // read number of elements + uint32_t len = 0; + ::memcpy(&len, data + 1, sizeof(len)); + AI_SWAP4(len); + + contentLength = len; + + content = new uint8_t[len]; + ::memcpy(content, data + 5, len); + } + } catch (...) { + // //we don't need the content data for contents that has already been loaded + // ASSIMP_LOG_VERBOSE_DEBUG_F("Caught exception in FBXMaterial (likely because content was already loaded): ", + // runtimeError.what()); + } + } + + props = GetPropertyTable(doc, "Video.FbxVideo", element, sc); +} + +Video::~Video() { + if (content) { + delete[] content; + } + + if (props != nullptr) { + delete props; + props = nullptr; + } +} +} // namespace FBXDocParser diff --git a/modules/fbx/fbx_parser/FBXMeshGeometry.cpp b/modules/fbx/fbx_parser/FBXMeshGeometry.cpp new file mode 100644 index 0000000000..ccc06550fe --- /dev/null +++ b/modules/fbx/fbx_parser/FBXMeshGeometry.cpp @@ -0,0 +1,485 @@ +/*************************************************************************/ +/* FBXMeshGeometry.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXMeshGeometry.cpp + * @brief Assimp::FBX::MeshGeometry implementation + */ + +#include <functional> + +#include "FBXDocument.h" +#include "FBXDocumentUtil.h" +#include "FBXImportSettings.h" +#include "FBXMeshGeometry.h" +#include "core/math/vector3.h" + +namespace FBXDocParser { + +using namespace Util; + +// ------------------------------------------------------------------------------------------------ +Geometry::Geometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) : + Object(id, element, name), skin() { + const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Deformer"); + for (const Connection *con : conns) { + const Skin *sk = ProcessSimpleConnection<Skin>(*con, false, "Skin -> Geometry", element); + if (sk) { + skin = sk; + } + const BlendShape *bsp = ProcessSimpleConnection<BlendShape>(*con, false, "BlendShape -> Geometry", + element); + if (bsp) { + blendShapes.push_back(bsp); + } + } +} + +// ------------------------------------------------------------------------------------------------ +Geometry::~Geometry() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +const std::vector<const BlendShape *> &Geometry::get_blend_shapes() const { + return blendShapes; +} + +// ------------------------------------------------------------------------------------------------ +const Skin *Geometry::DeformerSkin() const { + return skin; +} + +// ------------------------------------------------------------------------------------------------ +MeshGeometry::MeshGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) : + Geometry(id, element, name, doc) { + print_verbose("mesh name: " + String(name.c_str())); + + ScopePtr sc = element->Compound(); + ERR_FAIL_COND_MSG(sc == nullptr, "failed to read geometry, prevented crash"); + ERR_FAIL_COND_MSG(!HasElement(sc, "Vertices"), "Detected mesh with no vertexes, didn't populate the mesh"); + + // must have Mesh elements: + const ElementPtr Vertices = GetRequiredElement(sc, "Vertices", element); + const ElementPtr PolygonVertexIndex = GetRequiredElement(sc, "PolygonVertexIndex", element); + + if (HasElement(sc, "Edges")) { + const ElementPtr element_edges = GetRequiredElement(sc, "Edges", element); + ParseVectorDataArray(m_edges, element_edges); + } + + // read mesh data into arrays + ParseVectorDataArray(m_vertices, Vertices); + ParseVectorDataArray(m_face_indices, PolygonVertexIndex); + + ERR_FAIL_COND_MSG(m_vertices.empty(), "mesh with no vertexes in FBX file, did you mean to delete it?"); + ERR_FAIL_COND_MSG(m_face_indices.empty(), "mesh has no faces, was this intended?"); + + // Retrieve layer elements, for all of the mesh + const ElementCollection &Layer = sc->GetCollection("Layer"); + + // Store all layers + std::vector<std::tuple<int, std::string>> valid_layers; + + // now read the sub mesh information from the geometry (normals, uvs, etc) + for (ElementMap::const_iterator it = Layer.first; it != Layer.second; ++it) { + const ScopePtr layer = GetRequiredScope(it->second); + const ElementCollection &LayerElement = layer->GetCollection("LayerElement"); + for (ElementMap::const_iterator eit = LayerElement.first; eit != LayerElement.second; ++eit) { + std::string layer_name = eit->first; + ElementPtr element_layer = eit->second; + const ScopePtr layer_element = GetRequiredScope(element_layer); + + // Actual usable 'type' LayerElementUV, LayerElementNormal, etc + const ElementPtr Type = GetRequiredElement(layer_element, "Type"); + const ElementPtr TypedIndex = GetRequiredElement(layer_element, "TypedIndex"); + const std::string &type = ParseTokenAsString(GetRequiredToken(Type, 0)); + const int typedIndex = ParseTokenAsInt(GetRequiredToken(TypedIndex, 0)); + + // we only need the layer name and the typed index. + valid_layers.push_back(std::tuple<int, std::string>(typedIndex, type)); + } + } + + // get object / mesh directly from the FBX by the element ID. + const ScopePtr top = GetRequiredScope(element); + + // iterate over all layers for the mesh (uvs, normals, smoothing groups, colors, etc) + for (size_t x = 0; x < valid_layers.size(); x++) { + const int layer_id = std::get<0>(valid_layers[x]); + const std::string &layer_type_name = std::get<1>(valid_layers[x]); + + // Get collection of elements from the XLayerMap (example: LayerElementUV) + // this must contain our proper elements. + + // This is stupid, because it means we select them ALL not just the one we want. + // but it's fine we can match by id. + + const ElementCollection &candidates = top->GetCollection(layer_type_name); + + ElementMap::const_iterator iter; + for (iter = candidates.first; iter != candidates.second; ++iter) { + const ScopePtr layer_scope = GetRequiredScope(iter->second); + TokenPtr layer_token = GetRequiredToken(iter->second, 0); + const int index = ParseTokenAsInt(layer_token); + + ERR_FAIL_COND_MSG(layer_scope == nullptr, "prevented crash, layer scope is invalid"); + + if (index == layer_id) { + const std::string &MappingInformationType = ParseTokenAsString(GetRequiredToken( + GetRequiredElement(layer_scope, "MappingInformationType"), 0)); + + const std::string &ReferenceInformationType = ParseTokenAsString(GetRequiredToken( + GetRequiredElement(layer_scope, "ReferenceInformationType"), 0)); + + if (layer_type_name == "LayerElementUV") { + if (index == 0) { + m_uv_0 = resolve_vertex_data_array<Vector2>(layer_scope, MappingInformationType, ReferenceInformationType, "UV"); + } else if (index == 1) { + m_uv_1 = resolve_vertex_data_array<Vector2>(layer_scope, MappingInformationType, ReferenceInformationType, "UV"); + } + } else if (layer_type_name == "LayerElementMaterial") { + m_material_allocation_ids = resolve_vertex_data_array<int>(layer_scope, MappingInformationType, ReferenceInformationType, "Materials"); + } else if (layer_type_name == "LayerElementNormal") { + m_normals = resolve_vertex_data_array<Vector3>(layer_scope, MappingInformationType, ReferenceInformationType, "Normals"); + } else if (layer_type_name == "LayerElementColor") { + m_colors = resolve_vertex_data_array<Color>(layer_scope, MappingInformationType, ReferenceInformationType, "Colors", "ColorIndex"); + // NOTE: this is a useful sanity check to ensure you're getting any color data which is not default. + // const Color first_color_check = m_colors.data[0]; + // bool colors_are_all_the_same = true; + // size_t i = 1; + // for(i = 1; i < m_colors.data.size(); i++) + // { + // const Color current_color = m_colors.data[i]; + // if(current_color.is_equal_approx(first_color_check)) + // { + // continue; + // } + // else + // { + // colors_are_all_the_same = false; + // break; + // } + // } + // + // if(colors_are_all_the_same) + // { + // print_error("Color serialisation is not working for vertex colors some should be different in the test asset."); + // } + // else + // { + // print_verbose("Color array has unique colors at index: " + itos(i)); + // } + } + } + } + } + + print_verbose("Mesh statistics \nuv_0: " + m_uv_0.debug_info() + "\nuv_1: " + m_uv_1.debug_info() + "\nvertices: " + itos(m_vertices.size())); + + // Compose the edge of the mesh. + // You can see how the edges are stored into the FBX here: https://gist.github.com/AndreaCatania/da81840f5aa3b2feedf189e26c5a87e6 + for (size_t i = 0; i < m_edges.size(); i += 1) { + ERR_FAIL_INDEX_MSG((size_t)m_edges[i], m_face_indices.size(), "The edge is pointing to a weird location in the face indices. The FBX is corrupted."); + int polygon_vertex_0 = m_face_indices[m_edges[i]]; + int polygon_vertex_1; + if (polygon_vertex_0 < 0) { + // The polygon_vertex_0 points to the end of a polygon, so it's + // connected with the beginning of polygon in the edge list. + + // Fist invert the vertex. + polygon_vertex_0 = ~polygon_vertex_0; + + // Search the start vertex of the polygon. + // Iterate from the polygon_vertex_index backward till the start of + // the polygon is found. + ERR_FAIL_COND_MSG(m_edges[i] - 1 < 0, "The polygon is not yet started and we already need the final vertex. This FBX is corrupted."); + bool found_it = false; + for (int x = m_edges[i] - 1; x >= 0; x -= 1) { + if (x == 0) { + // This for sure is the start. + polygon_vertex_1 = m_face_indices[x]; + found_it = true; + break; + } else if (m_face_indices[x] < 0) { + // This is the end of the previous polygon, so the next is + // the start of the polygon we need. + polygon_vertex_1 = m_face_indices[x + 1]; + found_it = true; + break; + } + } + // As the algorithm above, this check is useless. Because the first + // ever vertex is always considered the begining of a polygon. + ERR_FAIL_COND_MSG(found_it == false, "Was not possible to find the first vertex of this polygon. FBX file is corrupted."); + + } else { + ERR_FAIL_INDEX_MSG((size_t)(m_edges[i] + 1), m_face_indices.size(), "FBX The other FBX edge seems to point to an invalid vertices. This FBX file is corrupted."); + // Take the next vertex + polygon_vertex_1 = m_face_indices[m_edges[i] + 1]; + } + + if (polygon_vertex_1 < 0) { + // We don't care if the `polygon_vertex_1` is the end of the polygon, + // for `polygon_vertex_1` so we can just invert it. + polygon_vertex_1 = ~polygon_vertex_1; + } + + ERR_FAIL_COND_MSG(polygon_vertex_0 == polygon_vertex_1, "The vertices of this edge can't be the same, Is this a point???. This FBX file is corrupted."); + + // Just create the edge. + edge_map.push_back({ polygon_vertex_0, polygon_vertex_1 }); + } +} + +MeshGeometry::~MeshGeometry() { + // empty +} + +const std::vector<Vector3> &MeshGeometry::get_vertices() const { + return m_vertices; +} + +const std::vector<MeshGeometry::Edge> &MeshGeometry::get_edge_map() const { + return edge_map; +} + +const std::vector<int> &MeshGeometry::get_polygon_indices() const { + return m_face_indices; +} + +const std::vector<int> &MeshGeometry::get_edges() const { + return m_edges; +} + +const MeshGeometry::MappingData<Vector3> &MeshGeometry::get_normals() const { + return m_normals; +} + +const MeshGeometry::MappingData<Vector2> &MeshGeometry::get_uv_0() const { + //print_verbose("get uv_0 " + m_uv_0.debug_info() ); + return m_uv_0; +} + +const MeshGeometry::MappingData<Vector2> &MeshGeometry::get_uv_1() const { + //print_verbose("get uv_1 " + m_uv_1.debug_info() ); + return m_uv_1; +} + +const MeshGeometry::MappingData<Color> &MeshGeometry::get_colors() const { + return m_colors; +} + +const MeshGeometry::MappingData<int> &MeshGeometry::get_material_allocation_id() const { + return m_material_allocation_ids; +} + +int MeshGeometry::get_edge_id(const std::vector<Edge> &p_map, int p_vertex_a, int p_vertex_b) { + for (size_t i = 0; i < p_map.size(); i += 1) { + if ((p_map[i].vertex_0 == p_vertex_a && p_map[i].vertex_1 == p_vertex_b) || (p_map[i].vertex_1 == p_vertex_a && p_map[i].vertex_0 == p_vertex_b)) { + return i; + } + } + return -1; +} + +MeshGeometry::Edge MeshGeometry::get_edge(const std::vector<Edge> &p_map, int p_id) { + ERR_FAIL_INDEX_V_MSG((size_t)p_id, p_map.size(), Edge({ -1, -1 }), "ID not found."); + return p_map[p_id]; +} + +template <class T> +MeshGeometry::MappingData<T> MeshGeometry::resolve_vertex_data_array( + const ScopePtr source, + const std::string &MappingInformationType, + const std::string &ReferenceInformationType, + const std::string &dataElementName, + const std::string &indexOverride) { + ERR_FAIL_COND_V_MSG(source == nullptr, MappingData<T>(), "Invalid scope operator preventing memory corruption"); + + // UVIndex, MaterialIndex, NormalIndex, etc.. + std::string indexDataElementName; + + if (indexOverride != "") { + // Colors should become ColorIndex + indexDataElementName = indexOverride; + } else { + // Some indexes will exist. + indexDataElementName = dataElementName + "Index"; + } + + // goal: expand everything to be per vertex + + ReferenceType l_ref_type = ReferenceType::direct; + + // Read the reference type into the enumeration + if (ReferenceInformationType == "IndexToDirect") { + l_ref_type = ReferenceType::index_to_direct; + } else if (ReferenceInformationType == "Index") { + // set non legacy index to direct mapping + l_ref_type = ReferenceType::index; + } else if (ReferenceInformationType == "Direct") { + l_ref_type = ReferenceType::direct; + } else { + ERR_FAIL_V_MSG(MappingData<T>(), "invalid reference type has the FBX format changed?"); + } + + MapType l_map_type = MapType::none; + + if (MappingInformationType == "None") { + l_map_type = MapType::none; + } else if (MappingInformationType == "ByVertice") { + l_map_type = MapType::vertex; + } else if (MappingInformationType == "ByPolygonVertex") { + l_map_type = MapType::polygon_vertex; + } else if (MappingInformationType == "ByPolygon") { + l_map_type = MapType::polygon; + } else if (MappingInformationType == "ByEdge") { + l_map_type = MapType::edge; + } else if (MappingInformationType == "AllSame") { + l_map_type = MapType::all_the_same; + } else { + print_error("invalid mapping type: " + String(MappingInformationType.c_str())); + } + + // create mapping data + MeshGeometry::MappingData<T> tempData; + tempData.map_type = l_map_type; + tempData.ref_type = l_ref_type; + + // parse data into array + ParseVectorDataArray(tempData.data, GetRequiredElement(source, dataElementName)); + + // index array wont always exist + const ElementPtr element = GetOptionalElement(source, indexDataElementName); + if (element) { + ParseVectorDataArray(tempData.index, element); + } + + return tempData; +} +// ------------------------------------------------------------------------------------------------ +ShapeGeometry::ShapeGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) : + Geometry(id, element, name, doc) { + const ScopePtr sc = element->Compound(); + if (nullptr == sc) { + DOMError("failed to read Geometry object (class: Shape), no data scope found"); + } + const ElementPtr Indexes = GetRequiredElement(sc, "Indexes", element); + const ElementPtr Normals = GetRequiredElement(sc, "Normals", element); + const ElementPtr Vertices = GetRequiredElement(sc, "Vertices", element); + ParseVectorDataArray(m_indices, Indexes); + ParseVectorDataArray(m_vertices, Vertices); + ParseVectorDataArray(m_normals, Normals); +} + +// ------------------------------------------------------------------------------------------------ +ShapeGeometry::~ShapeGeometry() { + // empty +} +// ------------------------------------------------------------------------------------------------ +const std::vector<Vector3> &ShapeGeometry::GetVertices() const { + return m_vertices; +} +// ------------------------------------------------------------------------------------------------ +const std::vector<Vector3> &ShapeGeometry::GetNormals() const { + return m_normals; +} +// ------------------------------------------------------------------------------------------------ +const std::vector<unsigned int> &ShapeGeometry::GetIndices() const { + return m_indices; +} +// ------------------------------------------------------------------------------------------------ +LineGeometry::LineGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) : + Geometry(id, element, name, doc) { + const ScopePtr sc = element->Compound(); + if (!sc) { + DOMError("failed to read Geometry object (class: Line), no data scope found"); + } + const ElementPtr Points = GetRequiredElement(sc, "Points", element); + const ElementPtr PointsIndex = GetRequiredElement(sc, "PointsIndex", element); + ParseVectorDataArray(m_vertices, Points); + ParseVectorDataArray(m_indices, PointsIndex); +} + +// ------------------------------------------------------------------------------------------------ +LineGeometry::~LineGeometry() { + // empty +} +// ------------------------------------------------------------------------------------------------ +const std::vector<Vector3> &LineGeometry::GetVertices() const { + return m_vertices; +} +// ------------------------------------------------------------------------------------------------ +const std::vector<int> &LineGeometry::GetIndices() const { + return m_indices; +} +} // namespace FBXDocParser diff --git a/modules/fbx/fbx_parser/FBXMeshGeometry.h b/modules/fbx/fbx_parser/FBXMeshGeometry.h new file mode 100644 index 0000000000..710e644c68 --- /dev/null +++ b/modules/fbx/fbx_parser/FBXMeshGeometry.h @@ -0,0 +1,263 @@ +/*************************************************************************/ +/* FBXMeshGeometry.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#ifndef FBX_MESH_GEOMETRY_H +#define FBX_MESH_GEOMETRY_H + +#include "core/math/color.h" +#include "core/math/vector2.h" +#include "core/math/vector3.h" +#include "core/templates/vector.h" + +#include "FBXDocument.h" +#include "FBXParser.h" + +#include <iostream> + +#define AI_MAX_NUMBER_OF_TEXTURECOORDS 4 +#define AI_MAX_NUMBER_OF_COLOR_SETS 8 + +namespace FBXDocParser { + +/* + * DOM base class for all kinds of FBX geometry + */ +class Geometry : public Object { +public: + Geometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc); + virtual ~Geometry(); + + /** Get the Skin attached to this geometry or NULL */ + const Skin *DeformerSkin() const; + + const std::vector<const BlendShape *> &get_blend_shapes() const; + + size_t get_blend_shape_count() const { + return blendShapes.size(); + } + +private: + const Skin *skin = nullptr; + std::vector<const BlendShape *> blendShapes; +}; + +typedef std::vector<int> MatIndexArray; + +/// Map Geometry stores the FBX file information. +/// +/// # FBX doc. +/// ## Reference type declared: +/// - Direct (directly related to the mapping information type) +/// - IndexToDirect (Map with key value, meaning depends on the MappingInformationType) +/// +/// ## Map Type: +/// * None The mapping is undetermined. +/// * ByVertex There will be one mapping coordinate for each surface control point/vertex (ControlPoint is a vertex). +/// * If you have direct reference type verticies[x] +/// * If you have IndexToDirect reference type the UV +/// * ByPolygonVertex There will be one mapping coordinate for each vertex, for every polygon of which it is a part. This means that a vertex will have as many mapping coordinates as polygons of which it is a part. (Sorted by polygon, referencing vertex) +/// * ByPolygon There can be only one mapping coordinate for the whole polygon. +/// * One mapping per polygon polygon x has this normal x +/// * For each vertex of the polygon then set the normal to x +/// * ByEdge There will be one mapping coordinate for each unique edge in the mesh. This is meant to be used with smoothing layer elements. (Mapping is referencing the edge id) +/// * AllSame There can be only one mapping coordinate for the whole surface. +class MeshGeometry : public Geometry { +public: + enum class MapType { + none = 0, // No mapping type. Stored as "None". + vertex, // Maps per vertex. Stored as "ByVertice". + polygon_vertex, // Maps per polygon vertex. Stored as "ByPolygonVertex". + polygon, // Maps per polygon. Stored as "ByPolygon". + edge, // Maps per edge. Stored as "ByEdge". + all_the_same // Uaps to everything. Stored as "AllSame". + }; + + enum class ReferenceType { + direct = 0, + index = 1, + index_to_direct = 2 + }; + + template <class T> + struct MappingData { + MapType map_type = MapType::none; + ReferenceType ref_type = ReferenceType::direct; + std::vector<T> data; + /// The meaning of the indices depends from the `MapType`. + /// If `ref_type` is `direct` this map is hollow. + std::vector<int> index; + + String debug_info() const { + return "indexes: " + itos(index.size()) + " data: " + itos(data.size()); + } + }; + + struct Edge { + int vertex_0 = 0, vertex_1 = 0; + Edge(int v0, int v1) : + vertex_0(v0), vertex_1(v1) {} + Edge() {} + }; + +public: + MeshGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc); + + virtual ~MeshGeometry(); + + const std::vector<Vector3> &get_vertices() const; + const std::vector<Edge> &get_edge_map() const; + const std::vector<int> &get_polygon_indices() const; + const std::vector<int> &get_edges() const; + const MappingData<Vector3> &get_normals() const; + const MappingData<Vector2> &get_uv_0() const; + const MappingData<Vector2> &get_uv_1() const; + const MappingData<Color> &get_colors() const; + const MappingData<int> &get_material_allocation_id() const; + + /// Returns -1 if the vertices doesn't form an edge. Vertex order, doesn't + // matter. + static int get_edge_id(const std::vector<Edge> &p_map, int p_vertex_a, int p_vertex_b); + // Retuns the edge point bu that ID, or the edge with -1 vertices if the + // id is not valid. + static Edge get_edge(const std::vector<Edge> &p_map, int p_id); + +private: + // Read directly from the FBX file. + std::vector<Vector3> m_vertices; + std::vector<Edge> edge_map; + std::vector<int> m_face_indices; + std::vector<int> m_edges; + MappingData<Vector3> m_normals; + MappingData<Vector2> m_uv_0; // first uv coordinates + MappingData<Vector2> m_uv_1; // second uv coordinates + MappingData<Color> m_colors; // colors for the mesh + MappingData<int> m_material_allocation_ids; // slot of material used + + template <class T> + MappingData<T> resolve_vertex_data_array( + const ScopePtr source, + const std::string &MappingInformationType, + const std::string &ReferenceInformationType, + const std::string &dataElementName, + const std::string &indexOverride = ""); +}; + +/* + * DOM class for FBX geometry of type "Shape" + */ +class ShapeGeometry : public Geometry { +public: + /** The class constructor */ + ShapeGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc); + + /** The class destructor */ + virtual ~ShapeGeometry(); + + /** Get a list of all vertex points, non-unique*/ + const std::vector<Vector3> &GetVertices() const; + + /** Get a list of all vertex normals or an empty array if + * no normals are specified. */ + const std::vector<Vector3> &GetNormals() const; + + /** Return list of vertex indices. */ + const std::vector<unsigned int> &GetIndices() const; + +private: + std::vector<Vector3> m_vertices; + std::vector<Vector3> m_normals; + std::vector<unsigned int> m_indices; +}; +/** +* DOM class for FBX geometry of type "Line" +*/ +class LineGeometry : public Geometry { +public: + /** The class constructor */ + LineGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc); + + /** The class destructor */ + virtual ~LineGeometry(); + + /** Get a list of all vertex points, non-unique*/ + const std::vector<Vector3> &GetVertices() const; + + /** Return list of vertex indices. */ + const std::vector<int> &GetIndices() const; + +private: + std::vector<Vector3> m_vertices; + std::vector<int> m_indices; +}; +} // namespace FBXDocParser + +#endif // FBX_MESH_GEOMETRY_H diff --git a/modules/fbx/fbx_parser/FBXModel.cpp b/modules/fbx/fbx_parser/FBXModel.cpp new file mode 100644 index 0000000000..767994441f --- /dev/null +++ b/modules/fbx/fbx_parser/FBXModel.cpp @@ -0,0 +1,176 @@ +/*************************************************************************/ +/* FBXModel.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXModel.cpp + * @brief Assimp::FBX::Model implementation + */ + +#include "FBXDocument.h" +#include "FBXDocumentUtil.h" +#include "FBXMeshGeometry.h" +#include "FBXParser.h" + +namespace FBXDocParser { + +using namespace Util; + +// ------------------------------------------------------------------------------------------------ +Model::Model(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : + Object(id, element, name), shading("Y") { + const ScopePtr sc = GetRequiredScope(element); + const ElementPtr Shading = sc->GetElement("Shading"); + const ElementPtr Culling = sc->GetElement("Culling"); + + if (Shading) { + shading = GetRequiredToken(Shading, 0)->StringContents(); + } + + if (Culling) { + culling = ParseTokenAsString(GetRequiredToken(Culling, 0)); + } + + props = GetPropertyTable(doc, "Model.FbxNode", element, sc); + ResolveLinks(element, doc); +} + +// ------------------------------------------------------------------------------------------------ +Model::~Model() { + if (props != nullptr) { + delete props; + props = nullptr; + } +} + +ModelLimbNode::ModelLimbNode(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : + Model(id, element, doc, name){}; + +ModelLimbNode::~ModelLimbNode() { +} + +// ------------------------------------------------------------------------------------------------ +void Model::ResolveLinks(const ElementPtr element, const Document &doc) { + const char *const arr[] = { "Geometry", "Material", "NodeAttribute" }; + + // resolve material + const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), arr, 3); + + materials.reserve(conns.size()); + geometry.reserve(conns.size()); + attributes.reserve(conns.size()); + for (const Connection *con : conns) { + // material and geometry links should be Object-Object connections + if (con->PropertyName().length()) { + continue; + } + + const Object *const ob = con->SourceObject(); + if (!ob) { + //DOMWarning("failed to read source object for incoming Model link, ignoring",&element); + continue; + } + + const Material *const mat = dynamic_cast<const Material *>(ob); + if (mat) { + materials.push_back(mat); + continue; + } + + const Geometry *const geo = dynamic_cast<const Geometry *>(ob); + if (geo) { + geometry.push_back(geo); + continue; + } + + const NodeAttribute *const att = dynamic_cast<const NodeAttribute *>(ob); + if (att) { + attributes.push_back(att); + continue; + } + + DOMWarning("source object for model link is neither Material, NodeAttribute nor Geometry, ignoring", element); + continue; + } +} + +// ------------------------------------------------------------------------------------------------ +bool Model::IsNull() const { + const std::vector<const NodeAttribute *> &attrs = GetAttributes(); + for (const NodeAttribute *att : attrs) { + const Null *null_tag = dynamic_cast<const Null *>(att); + if (null_tag) { + return true; + } + } + + return false; +} +} // namespace FBXDocParser diff --git a/modules/fbx/fbx_parser/FBXNodeAttribute.cpp b/modules/fbx/fbx_parser/FBXNodeAttribute.cpp new file mode 100644 index 0000000000..2749fc9f4d --- /dev/null +++ b/modules/fbx/fbx_parser/FBXNodeAttribute.cpp @@ -0,0 +1,183 @@ +/*************************************************************************/ +/* FBXNodeAttribute.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXNoteAttribute.cpp + * @brief Assimp::FBX::NodeAttribute (and subclasses) implementation + */ + +#include "FBXDocument.h" +#include "FBXDocumentUtil.h" +#include "FBXParser.h" +#include <iostream> + +namespace FBXDocParser { +using namespace Util; + +// ------------------------------------------------------------------------------------------------ +NodeAttribute::NodeAttribute(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : + Object(id, element, name), props() { + const ScopePtr sc = GetRequiredScope(element); + + const std::string &classname = ParseTokenAsString(GetRequiredToken(element, 2)); + + // hack on the deriving type but Null/LimbNode attributes are the only case in which + // the property table is by design absent and no warning should be generated + // for it. + const bool is_null_or_limb = !strcmp(classname.c_str(), "Null") || !strcmp(classname.c_str(), "LimbNode"); + props = GetPropertyTable(doc, "NodeAttribute.Fbx" + classname, element, sc, is_null_or_limb); +} + +// ------------------------------------------------------------------------------------------------ +NodeAttribute::~NodeAttribute() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +CameraSwitcher::CameraSwitcher(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : + NodeAttribute(id, element, doc, name) { + const ScopePtr sc = GetRequiredScope(element); + const ElementPtr CameraId = sc->GetElement("CameraId"); + const ElementPtr CameraName = sc->GetElement("CameraName"); + const ElementPtr CameraIndexName = sc->GetElement("CameraIndexName"); + + if (CameraId) { + cameraId = ParseTokenAsInt(GetRequiredToken(CameraId, 0)); + } + + if (CameraName) { + cameraName = GetRequiredToken(CameraName, 0)->StringContents(); + } + + if (CameraIndexName && CameraIndexName->Tokens().size()) { + cameraIndexName = GetRequiredToken(CameraIndexName, 0)->StringContents(); + } +} + +// ------------------------------------------------------------------------------------------------ +CameraSwitcher::~CameraSwitcher() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +Camera::Camera(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : + NodeAttribute(id, element, doc, name) { + // empty +} + +// ------------------------------------------------------------------------------------------------ +Camera::~Camera() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +Light::Light(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : + NodeAttribute(id, element, doc, name) { + // empty +} + +// ------------------------------------------------------------------------------------------------ +Light::~Light() { +} + +// ------------------------------------------------------------------------------------------------ +Null::Null(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : + NodeAttribute(id, element, doc, name) { +} + +// ------------------------------------------------------------------------------------------------ +Null::~Null() { +} + +// ------------------------------------------------------------------------------------------------ +LimbNode::LimbNode(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : + NodeAttribute(id, element, doc, name) { + //std::cout << "limb node: " << name << std::endl; + //const Scope &sc = GetRequiredScope(element); + + //const ElementPtr const TypeFlag = sc["TypeFlags"]; + + // keep this it can dump new properties for you + // for( auto element : sc.Elements()) + // { + // std::cout << "limbnode element: " << element.first << std::endl; + // } + + // if(TypeFlag) + // { + // // std::cout << "type flag: " << GetRequiredToken(*TypeFlag, 0).StringContents() << std::endl; + // } +} + +// ------------------------------------------------------------------------------------------------ +LimbNode::~LimbNode() { +} +} // namespace FBXDocParser diff --git a/modules/fbx/fbx_parser/FBXParseTools.h b/modules/fbx/fbx_parser/FBXParseTools.h new file mode 100644 index 0000000000..21472f5b7b --- /dev/null +++ b/modules/fbx/fbx_parser/FBXParseTools.h @@ -0,0 +1,111 @@ +/*************************************************************************/ +/* FBXParseTools.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef FBX_PARSE_TOOLS_H +#define FBX_PARSE_TOOLS_H + +#include "core/error/error_macros.h" +#include "core/string/ustring.h" + +#include <stdint.h> +#include <algorithm> +#include <locale> + +template <class char_t> +inline bool IsNewLine(char_t c) { + return c == '\n' || c == '\r'; +} +template <class char_t> +inline bool IsSpace(char_t c) { + return (c == (char_t)' ' || c == (char_t)'\t'); +} + +template <class char_t> +inline bool IsSpaceOrNewLine(char_t c) { + return IsNewLine(c) || IsSpace(c); +} + +template <class char_t> +inline bool IsLineEnd(char_t c) { + return (c == (char_t)'\r' || c == (char_t)'\n' || c == (char_t)'\0' || c == (char_t)'\f'); +} + +// ------------------------------------------------------------------------------------ +// Special version of the function, providing higher accuracy and safety +// It is mainly used by fast_atof to prevent ugly and unwanted integer overflows. +// ------------------------------------------------------------------------------------ +inline uint64_t strtoul10_64(const char *in, bool &errored, const char **out = 0, unsigned int *max_inout = 0) { + unsigned int cur = 0; + uint64_t value = 0; + + errored = *in < '0' || *in > '9'; + ERR_FAIL_COND_V_MSG(errored, 0, "The string cannot be converted parser error"); + + for (;;) { + if (*in < '0' || *in > '9') { + break; + } + + const uint64_t new_value = (value * (uint64_t)10) + ((uint64_t)(*in - '0')); + + // numeric overflow, we rely on you + if (new_value < value) { + //WARN_PRINT( "Converting the string \" " + in + " \" into a value resulted in overflow." ); + return 0; + } + + value = new_value; + + ++in; + ++cur; + + if (max_inout && *max_inout == cur) { + if (out) { /* skip to end */ + while (*in >= '0' && *in <= '9') { + ++in; + } + *out = in; + } + + return value; + } + } + if (out) { + *out = in; + } + + if (max_inout) { + *max_inout = cur; + } + + return value; +} + +#endif // FBX_PARSE_TOOLS_H diff --git a/modules/fbx/fbx_parser/FBXParser.cpp b/modules/fbx/fbx_parser/FBXParser.cpp new file mode 100644 index 0000000000..44c24ff926 --- /dev/null +++ b/modules/fbx/fbx_parser/FBXParser.cpp @@ -0,0 +1,1295 @@ +/*************************************************************************/ +/* FBXParser.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXParser.cpp + * @brief Implementation of the FBX parser and the rudimentary DOM that we use + */ + +#include "thirdparty/zlib/zlib.h" +#include <stdlib.h> /* strtol */ + +#include "ByteSwapper.h" +#include "FBXParseTools.h" +#include "FBXParser.h" +#include "FBXTokenizer.h" +#include "core/math/math_defs.h" +#include "core/math/transform.h" +#include "core/math/vector3.h" +#include "core/string/print_string.h" + +using namespace FBXDocParser; +namespace { + +// Initially, we did reinterpret_cast, breaking strict aliasing rules. +// This actually caused trouble on Android, so let's be safe this time. +// https://github.com/assimp/assimp/issues/24 +template <typename T> +T SafeParse(const char *data, const char *end) { + // Actual size validation happens during Tokenization so + // this is valid as an assertion. + (void)(end); + //ai_assert(static_cast<size_t>(end - data) >= sizeof(T)); + T result = static_cast<T>(0); + ::memcpy(&result, data, sizeof(T)); + return result; +} +} // namespace + +namespace FBXDocParser { + +// ------------------------------------------------------------------------------------------------ +Element::Element(const TokenPtr key_token, Parser &parser) : + key_token(key_token) { + TokenPtr n = nullptr; + do { + n = parser.AdvanceToNextToken(); + if (n == nullptr) { + continue; + } + + if (!n) { + print_error("unexpected end of file, expected closing bracket" + String(parser.LastToken()->StringContents().c_str())); + } + + if (n && n->Type() == TokenType_DATA) { + tokens.push_back(n); + TokenPtr prev = n; + n = parser.AdvanceToNextToken(); + + if (n == nullptr) { + break; + } + + if (!n) { + print_error("unexpected end of file, expected bracket, comma or key" + String(parser.LastToken()->StringContents().c_str())); + } + + const TokenType ty = n->Type(); + + // some exporters are missing a comma on the next line + if (ty == TokenType_DATA && prev->Type() == TokenType_DATA && (n->Line() == prev->Line() + 1)) { + tokens.push_back(n); + continue; + } + + if (ty != TokenType_OPEN_BRACKET && ty != TokenType_CLOSE_BRACKET && ty != TokenType_COMMA && ty != TokenType_KEY) { + print_error("unexpected token; expected bracket, comma or key" + String(n->StringContents().c_str())); + } + } + + if (n && n->Type() == TokenType_OPEN_BRACKET) { + compound = new_Scope(parser); + parser.scopes.push_back(compound); + + // current token should be a TOK_CLOSE_BRACKET + n = parser.CurrentToken(); + + if (n && n->Type() != TokenType_CLOSE_BRACKET) { + print_error("expected closing bracket" + String(n->StringContents().c_str())); + } + + parser.AdvanceToNextToken(); + return; + } + } while (n && n->Type() != TokenType_KEY && n->Type() != TokenType_CLOSE_BRACKET); +} + +// ------------------------------------------------------------------------------------------------ +Element::~Element() { +} + +// ------------------------------------------------------------------------------------------------ +Scope::Scope(Parser &parser, bool topLevel) { + if (!topLevel) { + TokenPtr t = parser.CurrentToken(); + if (t->Type() != TokenType_OPEN_BRACKET) { + print_error("expected open bracket" + String(t->StringContents().c_str())); + } + } + + TokenPtr n = parser.AdvanceToNextToken(); + if (n == nullptr) { + print_error("unexpected end of file"); + } + + // note: empty scopes are allowed + while (n && n->Type() != TokenType_CLOSE_BRACKET) { + if (n->Type() != TokenType_KEY) { + print_error("unexpected token, expected TOK_KEY" + String(n->StringContents().c_str())); + } + + const std::string str = n->StringContents(); + + // std::multimap<std::string, ElementPtr> (key and value) + elements.insert(ElementMap::value_type(str, new_Element(n, parser))); + + // Element() should stop at the next Key token (or right after a Close token) + n = parser.CurrentToken(); + if (n == nullptr) { + if (topLevel) { + return; + } + + //print_error("unexpected end of file" + String(parser.LastToken()->StringContents().c_str())); + } + } +} + +// ------------------------------------------------------------------------------------------------ +Scope::~Scope() { + for (ElementMap::value_type &v : elements) { + delete v.second; + v.second = nullptr; + } + + elements.clear(); +} + +// ------------------------------------------------------------------------------------------------ +Parser::Parser(const TokenList &tokens, bool is_binary) : + tokens(tokens), last(), current(), cursor(tokens.begin()), is_binary(is_binary) { + root = new_Scope(*this, true); + scopes.push_back(root); +} + +// ------------------------------------------------------------------------------------------------ +Parser::~Parser() { + for (ScopePtr scope : scopes) { + delete scope; + scope = nullptr; + } +} + +// ------------------------------------------------------------------------------------------------ +TokenPtr Parser::AdvanceToNextToken() { + last = current; + if (cursor == tokens.end()) { + current = nullptr; + } else { + current = *cursor++; + } + return current; +} + +// ------------------------------------------------------------------------------------------------ +TokenPtr Parser::CurrentToken() const { + return current; +} + +// ------------------------------------------------------------------------------------------------ +TokenPtr Parser::LastToken() const { + return last; +} + +// ------------------------------------------------------------------------------------------------ +uint64_t ParseTokenAsID(const TokenPtr t, const char *&err_out) { + ERR_FAIL_COND_V_MSG(t == nullptr, 0L, "Invalid token passed to ParseTokenAsID"); + err_out = nullptr; + + if (t->Type() != TokenType_DATA) { + err_out = "expected TOK_DATA token"; + return 0L; + } + + if (t->IsBinary()) { + const char *data = t->begin(); + if (data[0] != 'L') { + err_out = "failed to parse ID, unexpected data type, expected L(ong) (binary)"; + return 0L; + } + + uint64_t id = SafeParse<uint64_t>(data + 1, t->end()); + return id; + } + + // XXX: should use size_t here + unsigned int length = static_cast<unsigned int>(t->end() - t->begin()); + //ai_assert(length > 0); + + const char *out = nullptr; + bool errored = false; + + const uint64_t id = strtoul10_64(t->begin(), errored, &out, &length); + if (errored || out > t->end()) { + err_out = "failed to parse ID (text)"; + return 0L; + } + + return id; +} + +// ------------------------------------------------------------------------------------------------ +// wrapper around ParseTokenAsID() with print_error handling +uint64_t ParseTokenAsID(const TokenPtr t) { + const char *err = nullptr; + const uint64_t i = ParseTokenAsID(t, err); + if (err) { + print_error(String(err) + " " + String(t->StringContents().c_str())); + } + return i; +} + +// ------------------------------------------------------------------------------------------------ +size_t ParseTokenAsDim(const TokenPtr t, const char *&err_out) { + // same as ID parsing, except there is a trailing asterisk + err_out = nullptr; + + if (t->Type() != TokenType_DATA) { + err_out = "expected TOK_DATA token"; + return 0; + } + + if (t->IsBinary()) { + const char *data = t->begin(); + if (data[0] != 'L') { + err_out = "failed to parse ID, unexpected data type, expected L(ong) (binary)"; + return 0; + } + + uint64_t id = SafeParse<uint64_t>(data + 1, t->end()); + AI_SWAP8(id); + return static_cast<size_t>(id); + } + + if (*t->begin() != '*') { + err_out = "expected asterisk before array dimension"; + return 0; + } + + // XXX: should use size_t here + unsigned int length = static_cast<unsigned int>(t->end() - t->begin()); + if (length == 0) { + err_out = "expected valid integer number after asterisk"; + return 0; + } + + const char *out = nullptr; + bool errored = false; + const size_t id = static_cast<size_t>(strtoul10_64(t->begin() + 1, errored, &out, &length)); + if (errored || out > t->end()) { + print_error("failed to parse id"); + err_out = "failed to parse ID"; + return 0; + } + + return id; +} + +// ------------------------------------------------------------------------------------------------ +float ParseTokenAsFloat(const TokenPtr t, const char *&err_out) { + err_out = nullptr; + + if (t->Type() != TokenType_DATA) { + err_out = "expected TOK_DATA token"; + return 0.0f; + } + + if (t->IsBinary()) { + const char *data = t->begin(); + if (data[0] != 'F' && data[0] != 'D') { + err_out = "failed to parse F(loat) or D(ouble), unexpected data type (binary)"; + return 0.0f; + } + + if (data[0] == 'F') { + return SafeParse<float>(data + 1, t->end()); + } else { + return static_cast<float>(SafeParse<double>(data + 1, t->end())); + } + } + +// need to copy the input string to a temporary buffer +// first - next in the fbx token stream comes ',', +// which fast_atof could interpret as decimal point. +#define MAX_FLOAT_LENGTH 31 + char temp[MAX_FLOAT_LENGTH + 1]; + const size_t length = static_cast<size_t>(t->end() - t->begin()); + std::copy(t->begin(), t->end(), temp); + temp[std::min(static_cast<size_t>(MAX_FLOAT_LENGTH), length)] = '\0'; + + return atof(temp); +} + +// ------------------------------------------------------------------------------------------------ +int ParseTokenAsInt(const TokenPtr t, const char *&err_out) { + err_out = nullptr; + + if (t->Type() != TokenType_DATA) { + err_out = "expected TOK_DATA token"; + return 0; + } + + // binary files are simple to parse + if (t->IsBinary()) { + const char *data = t->begin(); + if (data[0] != 'I') { + err_out = "failed to parse I(nt), unexpected data type (binary)"; + return 0; + } + + int32_t ival = SafeParse<int32_t>(data + 1, t->end()); + AI_SWAP4(ival); + return static_cast<int>(ival); + } + + // ASCII files are unsafe. + const size_t length = static_cast<size_t>(t->end() - t->begin()); + if (length == 0) { + err_out = "expected valid integer number after asterisk"; + ERR_FAIL_V_MSG(0, "expected valid integer number after asterisk"); + } + + // must not be null for strtol to work + char *out = (char *)t->end(); + // string begin, end ptr ref, base 10 + const int value = strtol(t->begin(), &out, 10); + if (out == nullptr || out != t->end()) { + err_out = "failed to parse ID"; + ERR_FAIL_V_MSG(0, "failed to parse ID"); + } + + return value; +} + +// ------------------------------------------------------------------------------------------------ +int64_t ParseTokenAsInt64(const TokenPtr t, const char *&err_out) { + err_out = nullptr; + + if (t->Type() != TokenType_DATA) { + err_out = "expected TOK_DATA token"; + return 0L; + } + + if (t->IsBinary()) { + const char *data = t->begin(); + if (data[0] != 'L') { + err_out = "failed to parse Int64, unexpected data type"; + return 0L; + } + + int64_t id = SafeParse<int64_t>(data + 1, t->end()); + AI_SWAP8(id); + return id; + } + + // XXX: should use size_t here + unsigned int length = static_cast<unsigned int>(t->end() - t->begin()); + //ai_assert(length > 0); + + char *out = nullptr; + const int64_t id = strtol(t->begin(), &out, length); + if (out > t->end()) { + err_out = "failed to parse Int64 (text)"; + return 0L; + } + + return id; +} + +// ------------------------------------------------------------------------------------------------ +std::string ParseTokenAsString(const TokenPtr t, const char *&err_out) { + err_out = nullptr; + + if (t->Type() != TokenType_DATA) { + err_out = "expected TOK_DATA token"; + return ""; + } + + if (t->IsBinary()) { + const char *data = t->begin(); + if (data[0] != 'S') { + err_out = "failed to parse String, unexpected data type (binary)"; + return ""; + } + + // read string length + int32_t len = SafeParse<int32_t>(data + 1, t->end()); + AI_SWAP4(len); + + //ai_assert(t.end() - data == 5 + len); + return std::string(data + 5, len); + } + + const size_t length = static_cast<size_t>(t->end() - t->begin()); + if (length < 2) { + err_out = "token is too short to hold a string"; + return ""; + } + + const char *s = t->begin(), *e = t->end() - 1; + if (*s != '\"' || *e != '\"') { + err_out = "expected double quoted string"; + return ""; + } + + return std::string(s + 1, length - 2); +} + +namespace { + +// ------------------------------------------------------------------------------------------------ +// read the type code and element count of a binary data array and stop there +void ReadBinaryDataArrayHead(const char *&data, const char *end, char &type, uint32_t &count, + const ElementPtr el) { + TokenPtr token = el->KeyToken(); + if (static_cast<size_t>(end - data) < 5) { + print_error("binary data array is too short, need five (5) bytes for type signature and element count: " + String(token->StringContents().c_str())); + } + + // data type + type = *data; + + // read number of elements + uint32_t len = SafeParse<uint32_t>(data + 1, end); + AI_SWAP4(len); + + count = len; + data += 5; +} + +// ------------------------------------------------------------------------------------------------ +// read binary data array, assume cursor points to the 'compression mode' field (i.e. behind the header) +void ReadBinaryDataArray(char type, uint32_t count, const char *&data, const char *end, + std::vector<char> &buff, + const ElementPtr /*el*/) { + uint32_t encmode = SafeParse<uint32_t>(data, end); + AI_SWAP4(encmode); + data += 4; + + // next comes the compressed length + uint32_t comp_len = SafeParse<uint32_t>(data, end); + AI_SWAP4(comp_len); + data += 4; + + //ai_assert(data + comp_len == end); + + // determine the length of the uncompressed data by looking at the type signature + uint32_t stride = 0; + switch (type) { + case 'f': + case 'i': + stride = 4; + break; + + case 'd': + case 'l': + stride = 8; + break; + } + + const uint32_t full_length = stride * count; + buff.resize(full_length); + + if (encmode == 0) { + //ai_assert(full_length == comp_len); + + // plain data, no compression + std::copy(data, end, buff.begin()); + } else if (encmode == 1) { + // zlib/deflate, next comes ZIP head (0x78 0x01) + // see http://www.ietf.org/rfc/rfc1950.txt + + z_stream zstream; + zstream.opaque = Z_NULL; + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.data_type = Z_BINARY; + + // http://hewgill.com/journal/entries/349-how-to-decompress-gzip-stream-with-zlib + if (Z_OK != inflateInit(&zstream)) { + print_error("failure initializing zlib"); + } + + zstream.next_in = reinterpret_cast<Bytef *>(const_cast<char *>(data)); + zstream.avail_in = comp_len; + + zstream.avail_out = static_cast<uInt>(buff.size()); + zstream.next_out = reinterpret_cast<Bytef *>(&*buff.begin()); + const int ret = inflate(&zstream, Z_FINISH); + + if (ret != Z_STREAM_END && ret != Z_OK) { + print_error("failure decompressing compressed data section"); + } + + // terminate zlib + inflateEnd(&zstream); + } +#ifdef ASSIMP_BUILD_DEBUG + else { + // runtime check for this happens at tokenization stage + //ai_assert(false); + } +#endif + + data += comp_len; + //ai_assert(data == end); +} +} // namespace + +// ------------------------------------------------------------------------------------------------ +// read an array of float3 tuples +void ParseVectorDataArray(std::vector<Vector3> &out, const ElementPtr el) { + out.resize(0); + + const TokenList &tok = el->Tokens(); + TokenPtr token = el->KeyToken(); + if (tok.empty()) { + print_error("unexpected empty element" + String(token->StringContents().c_str())); + } + + if (tok[0]->IsBinary()) { + const char *data = tok[0]->begin(), *end = tok[0]->end(); + + char type; + uint32_t count; + ReadBinaryDataArrayHead(data, end, type, count, el); + + if (count % 3 != 0) { + print_error("number of floats is not a multiple of three (3) (binary)" + String(token->StringContents().c_str())); + } + + if (!count) { + return; + } + + if (type != 'd' && type != 'f') { + print_error("expected float or double array (binary)" + String(token->StringContents().c_str())); + } + + std::vector<char> buff; + ReadBinaryDataArray(type, count, data, end, buff, el); + + //ai_assert(data == end); + //ai_assert(buff.size() == count * (type == 'd' ? 8 : 4)); + + const uint32_t count3 = count / 3; + out.reserve(count3); + + if (type == 'd') { + const double *d = reinterpret_cast<const double *>(&buff[0]); + for (unsigned int i = 0; i < count3; ++i, d += 3) { + out.push_back(Vector3(static_cast<real_t>(d[0]), + static_cast<real_t>(d[1]), + static_cast<real_t>(d[2]))); + } + // for debugging + /*for ( size_t i = 0; i < out.size(); i++ ) { + aiVector3D vec3( out[ i ] ); + std::stringstream stream; + stream << " vec3.x = " << vec3.x << " vec3.y = " << vec3.y << " vec3.z = " << vec3.z << std::endl; + DefaultLogger::get()->info( stream.str() ); + }*/ + } else if (type == 'f') { + const float *f = reinterpret_cast<const float *>(&buff[0]); + for (unsigned int i = 0; i < count3; ++i, f += 3) { + out.push_back(Vector3(f[0], f[1], f[2])); + } + } + + return; + } + + const size_t dim = ParseTokenAsDim(tok[0]); + + // may throw bad_alloc if the input is rubbish, but this need + // not to be prevented - importing would fail but we wouldn't + // crash since assimp handles this case properly. + out.reserve(dim); + + const ScopePtr scope = GetRequiredScope(el); + const ElementPtr a = GetRequiredElement(scope, "a", el); + + if (a->Tokens().size() % 3 != 0) { + print_error("number of floats is not a multiple of three (3)" + String(token->StringContents().c_str())); + } else { + for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) { + Vector3 v; + v.x = ParseTokenAsFloat(*it++); + v.y = ParseTokenAsFloat(*it++); + v.z = ParseTokenAsFloat(*it++); + + out.push_back(v); + } + } +} + +// ------------------------------------------------------------------------------------------------ +// read an array of color4 tuples +void ParseVectorDataArray(std::vector<Color> &out, const ElementPtr el) { + out.resize(0); + const TokenList &tok = el->Tokens(); + + TokenPtr token = el->KeyToken(); + + if (tok.empty()) { + print_error("unexpected empty element" + String(token->StringContents().c_str())); + } + + if (tok[0]->IsBinary()) { + const char *data = tok[0]->begin(), *end = tok[0]->end(); + + char type; + uint32_t count; + ReadBinaryDataArrayHead(data, end, type, count, el); + + if (count % 4 != 0) { + print_error("number of floats is not a multiple of four (4) (binary)" + String(token->StringContents().c_str())); + } + + if (!count) { + return; + } + + if (type != 'd' && type != 'f') { + print_error("expected float or double array (binary)" + String(token->StringContents().c_str())); + } + + std::vector<char> buff; + ReadBinaryDataArray(type, count, data, end, buff, el); + + //ai_assert(data == end); + //ai_assert(buff.size() == count * (type == 'd' ? 8 : 4)); + + const uint32_t count4 = count / 4; + out.reserve(count4); + + if (type == 'd') { + const double *d = reinterpret_cast<const double *>(&buff[0]); + for (unsigned int i = 0; i < count4; ++i, d += 4) { + out.push_back(Color(static_cast<float>(d[0]), + static_cast<float>(d[1]), + static_cast<float>(d[2]), + static_cast<float>(d[3]))); + } + } else if (type == 'f') { + const float *f = reinterpret_cast<const float *>(&buff[0]); + for (unsigned int i = 0; i < count4; ++i, f += 4) { + out.push_back(Color(f[0], f[1], f[2], f[3])); + } + } + return; + } + + const size_t dim = ParseTokenAsDim(tok[0]); + + // see notes in ParseVectorDataArray() above + out.reserve(dim); + + const ScopePtr scope = GetRequiredScope(el); + const ElementPtr a = GetRequiredElement(scope, "a", el); + + if (a->Tokens().size() % 4 != 0) { + print_error("number of floats is not a multiple of four (4)" + String(token->StringContents().c_str())); + } + for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) { + Color v; + v.r = ParseTokenAsFloat(*it++); + v.g = ParseTokenAsFloat(*it++); + v.b = ParseTokenAsFloat(*it++); + v.a = ParseTokenAsFloat(*it++); + + out.push_back(v); + } +} + +// ------------------------------------------------------------------------------------------------ +// read an array of float2 tuples +void ParseVectorDataArray(std::vector<Vector2> &out, const ElementPtr el) { + out.resize(0); + const TokenList &tok = el->Tokens(); + TokenPtr token = el->KeyToken(); + if (tok.empty()) { + print_error("unexpected empty element" + String(token->StringContents().c_str())); + } + + if (tok[0]->IsBinary()) { + const char *data = tok[0]->begin(), *end = tok[0]->end(); + + char type; + uint32_t count; + ReadBinaryDataArrayHead(data, end, type, count, el); + + if (count % 2 != 0) { + print_error("number of floats is not a multiple of two (2) (binary)" + String(token->StringContents().c_str())); + } + + if (!count) { + return; + } + + if (type != 'd' && type != 'f') { + print_error("expected float or double array (binary)" + String(token->StringContents().c_str())); + } + + std::vector<char> buff; + ReadBinaryDataArray(type, count, data, end, buff, el); + + //ai_assert(data == end); + //ai_assert(buff.size() == count * (type == 'd' ? 8 : 4)); + + const uint32_t count2 = count / 2; + out.reserve(count2); + + if (type == 'd') { + const double *d = reinterpret_cast<const double *>(&buff[0]); + for (unsigned int i = 0; i < count2; ++i, d += 2) { + out.push_back(Vector2(static_cast<float>(d[0]), + static_cast<float>(d[1]))); + } + } else if (type == 'f') { + const float *f = reinterpret_cast<const float *>(&buff[0]); + for (unsigned int i = 0; i < count2; ++i, f += 2) { + out.push_back(Vector2(f[0], f[1])); + } + } + + return; + } + + const size_t dim = ParseTokenAsDim(tok[0]); + + // see notes in ParseVectorDataArray() above + out.reserve(dim); + + const ScopePtr scope = GetRequiredScope(el); + const ElementPtr a = GetRequiredElement(scope, "a", el); + + if (a->Tokens().size() % 2 != 0) { + print_error("number of floats is not a multiple of two (2)" + String(token->StringContents().c_str())); + } else { + for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) { + Vector2 v; + v.x = ParseTokenAsFloat(*it++); + v.y = ParseTokenAsFloat(*it++); + out.push_back(v); + } + } +} + +// ------------------------------------------------------------------------------------------------ +// read an array of ints +void ParseVectorDataArray(std::vector<int> &out, const ElementPtr el) { + out.resize(0); + const TokenList &tok = el->Tokens(); + TokenPtr token = el->KeyToken(); + if (tok.empty()) { + print_error("unexpected empty element" + String(token->StringContents().c_str())); + } + + if (tok[0]->IsBinary()) { + const char *data = tok[0]->begin(), *end = tok[0]->end(); + + char type; + uint32_t count; + ReadBinaryDataArrayHead(data, end, type, count, el); + + if (!count) { + return; + } + + if (type != 'i') { + print_error("expected int array (binary)" + String(token->StringContents().c_str())); + } + + std::vector<char> buff; + ReadBinaryDataArray(type, count, data, end, buff, el); + + //ai_assert(data == end); + //ai_assert(buff.size() == count * 4); + + out.reserve(count); + + const int32_t *ip = reinterpret_cast<const int32_t *>(&buff[0]); + for (unsigned int i = 0; i < count; ++i, ++ip) { + int32_t val = *ip; + AI_SWAP4(val); + out.push_back(val); + } + + return; + } + + const size_t dim = ParseTokenAsDim(tok[0]); + + // see notes in ParseVectorDataArray() + out.reserve(dim); + + const ScopePtr scope = GetRequiredScope(el); + const ElementPtr a = GetRequiredElement(scope, "a", el); + + for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) { + const int ival = ParseTokenAsInt(*it++); + out.push_back(ival); + } +} + +// ------------------------------------------------------------------------------------------------ +// read an array of floats +void ParseVectorDataArray(std::vector<float> &out, const ElementPtr el) { + out.resize(0); + const TokenList &tok = el->Tokens(); + TokenPtr token = el->KeyToken(); + if (tok.empty()) { + print_error("unexpected empty element: " + String(token->StringContents().c_str())); + } + + if (tok[0]->IsBinary()) { + const char *data = tok[0]->begin(), *end = tok[0]->end(); + + char type; + uint32_t count; + ReadBinaryDataArrayHead(data, end, type, count, el); + + if (!count) { + return; + } + + if (type != 'd' && type != 'f') { + print_error("expected float or double array (binary) " + String(token->StringContents().c_str())); + } + + std::vector<char> buff; + ReadBinaryDataArray(type, count, data, end, buff, el); + + //ai_assert(data == end); + //ai_assert(buff.size() == count * (type == 'd' ? 8 : 4)); + + if (type == 'd') { + const double *d = reinterpret_cast<const double *>(&buff[0]); + for (unsigned int i = 0; i < count; ++i, ++d) { + out.push_back(static_cast<float>(*d)); + } + } else if (type == 'f') { + const float *f = reinterpret_cast<const float *>(&buff[0]); + for (unsigned int i = 0; i < count; ++i, ++f) { + out.push_back(*f); + } + } + + return; + } + + const size_t dim = ParseTokenAsDim(tok[0]); + + // see notes in ParseVectorDataArray() + out.reserve(dim); + + const ScopePtr scope = GetRequiredScope(el); + const ElementPtr a = GetRequiredElement(scope, "a", el); + + for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) { + const float ival = ParseTokenAsFloat(*it++); + out.push_back(ival); + } +} + +// ------------------------------------------------------------------------------------------------ +// read an array of uints +void ParseVectorDataArray(std::vector<unsigned int> &out, const ElementPtr el) { + out.resize(0); + const TokenList &tok = el->Tokens(); + const TokenPtr token = el->KeyToken(); + + ERR_FAIL_COND_MSG(!token, "invalid ParseVectorDataArrat token invalid"); + + if (tok.empty()) { + print_error("unexpected empty element: " + String(token->StringContents().c_str())); + } + + if (tok[0]->IsBinary()) { + const char *data = tok[0]->begin(), *end = tok[0]->end(); + + char type; + uint32_t count; + ReadBinaryDataArrayHead(data, end, type, count, el); + + if (!count) { + return; + } + + if (type != 'i') { + print_error("expected (u)int array (binary)" + String(token->StringContents().c_str())); + } + + std::vector<char> buff; + ReadBinaryDataArray(type, count, data, end, buff, el); + + //ai_assert(data == end); + //ai_assert(buff.size() == count * 4); + + out.reserve(count); + + const int32_t *ip = reinterpret_cast<const int32_t *>(&buff[0]); + for (unsigned int i = 0; i < count; ++i, ++ip) { + int32_t val = *ip; + if (val < 0) { + print_error("encountered negative integer index (binary)"); + } + + out.push_back(val); + } + + return; + } + + const size_t dim = ParseTokenAsDim(tok[0]); + + // see notes in ParseVectorDataArray() + out.reserve(dim); + + const ScopePtr scope = GetRequiredScope(el); + const ElementPtr a = GetRequiredElement(scope, "a", el); + + for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) { + const int ival = ParseTokenAsInt(*it++); + if (ival < 0) { + print_error("encountered negative integer index"); + } + out.push_back(static_cast<unsigned int>(ival)); + } +} + +// ------------------------------------------------------------------------------------------------ +// read an array of uint64_ts +void ParseVectorDataArray(std::vector<uint64_t> &out, const ElementPtr el) { + out.resize(0); + + const TokenList &tok = el->Tokens(); + TokenPtr token = el->KeyToken(); + ERR_FAIL_COND(!token); + + if (tok.empty()) { + print_error("unexpected empty element " + String(token->StringContents().c_str())); + } + + if (tok[0]->IsBinary()) { + const char *data = tok[0]->begin(), *end = tok[0]->end(); + + char type; + uint32_t count; + ReadBinaryDataArrayHead(data, end, type, count, el); + + if (!count) { + return; + } + + if (type != 'l') { + print_error("expected long array (binary): " + String(token->StringContents().c_str())); + } + + std::vector<char> buff; + ReadBinaryDataArray(type, count, data, end, buff, el); + + //ai_assert(data == end); + //ai_assert(buff.size() == count * 8); + + out.reserve(count); + + const uint64_t *ip = reinterpret_cast<const uint64_t *>(&buff[0]); + for (unsigned int i = 0; i < count; ++i, ++ip) { + uint64_t val = *ip; + AI_SWAP8(val); + out.push_back(val); + } + + return; + } + + const size_t dim = ParseTokenAsDim(tok[0]); + + // see notes in ParseVectorDataArray() + out.reserve(dim); + + const ScopePtr scope = GetRequiredScope(el); + const ElementPtr a = GetRequiredElement(scope, "a", el); + + for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) { + const uint64_t ival = ParseTokenAsID(*it++); + + out.push_back(ival); + } +} + +// ------------------------------------------------------------------------------------------------ +// read an array of int64_ts +void ParseVectorDataArray(std::vector<int64_t> &out, const ElementPtr el) { + out.resize(0); + const TokenList &tok = el->Tokens(); + TokenPtr token = el->KeyToken(); + ERR_FAIL_COND(!token); + if (tok.empty()) { + print_error("unexpected empty element: " + String(token->StringContents().c_str())); + } + + if (tok[0]->IsBinary()) { + const char *data = tok[0]->begin(), *end = tok[0]->end(); + + char type; + uint32_t count; + ReadBinaryDataArrayHead(data, end, type, count, el); + + if (!count) { + return; + } + + if (type != 'l') { + print_error("expected long array (binary) " + String(token->StringContents().c_str())); + } + + std::vector<char> buff; + ReadBinaryDataArray(type, count, data, end, buff, el); + + //ai_assert(data == end); + //ai_assert(buff.size() == count * 8); + + out.reserve(count); + + const int64_t *ip = reinterpret_cast<const int64_t *>(&buff[0]); + for (unsigned int i = 0; i < count; ++i, ++ip) { + int64_t val = *ip; + AI_SWAP8(val); + out.push_back(val); + } + + return; + } + + const size_t dim = ParseTokenAsDim(tok[0]); + + // see notes in ParseVectorDataArray() + out.reserve(dim); + + const ScopePtr scope = GetRequiredScope(el); + const ElementPtr a = GetRequiredElement(scope, "a", el); + + for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) { + const int64_t val = ParseTokenAsInt64(*it++); + out.push_back(val); + } +} + +// ------------------------------------------------------------------------------------------------ +Transform ReadMatrix(const ElementPtr element) { + std::vector<float> values; + ParseVectorDataArray(values, element); + + if (values.size() != 16) { + print_error("expected 16 matrix elements"); + } + + // clean values to prevent any IBM damage on inverse() / affine_inverse() + for (float &value : values) { + if (::Math::is_equal_approx(0, value)) { + value = 0; + } + } + + Transform xform; + Basis basis; + + basis.set( + Vector3(values[0], values[1], values[2]), + Vector3(values[4], values[5], values[6]), + Vector3(values[8], values[9], values[10])); + + xform.basis = basis; + xform.origin = Vector3(values[12], values[13], values[14]); + // determine if we need to think about this with dynamic rotation order? + // for example: + // xform.basis = z_axis * y_axis * x_axis; + //xform.basis.transpose(); + + print_verbose("xform verbose basis: " + (xform.basis.get_euler() * (180 / Math_PI)) + " xform origin:" + xform.origin); + + return xform; +} + +// ------------------------------------------------------------------------------------------------ +// wrapper around ParseTokenAsString() with print_error handling +std::string ParseTokenAsString(const TokenPtr t) { + ERR_FAIL_COND_V(!t, ""); + const char *err; + const std::string &i = ParseTokenAsString(t, err); + if (err) { + print_error(String(err) + ", " + String(t->StringContents().c_str())); + } + return i; +} + +// ------------------------------------------------------------------------------------------------ +// extract a required element from a scope, abort if the element cannot be found +ElementPtr GetRequiredElement(const ScopePtr sc, const std::string &index, const ElementPtr element /*= NULL*/) { + const ElementPtr el = sc->GetElement(index); + TokenPtr token = el->KeyToken(); + ERR_FAIL_COND_V(!token, nullptr); + if (!el) { + print_error("did not find required element \"" + String(index.c_str()) + "\" " + String(token->StringContents().c_str())); + } + return el; +} + +bool HasElement(const ScopePtr sc, const std::string &index) { + const ElementPtr el = sc->GetElement(index); + if (nullptr == el) { + return false; + } + + return true; +} + +// ------------------------------------------------------------------------------------------------ +// extract a required element from a scope, abort if the element cannot be found +ElementPtr GetOptionalElement(const ScopePtr sc, const std::string &index, const ElementPtr element /*= NULL*/) { + const ElementPtr el = sc->GetElement(index); + return el; +} + +// ------------------------------------------------------------------------------------------------ +// extract required compound scope +ScopePtr GetRequiredScope(const ElementPtr el) { + if (el) { + ScopePtr s = el->Compound(); + TokenPtr token = el->KeyToken(); + ERR_FAIL_COND_V(!token, nullptr); + if (s) { + return s; + } + + ERR_FAIL_V_MSG(nullptr, "expected compound scope " + String(token->StringContents().c_str())); + } + + ERR_FAIL_V_MSG(nullptr, "Invalid element supplied to parser"); +} + +// ------------------------------------------------------------------------------------------------ +// get token at a particular index +TokenPtr GetRequiredToken(const ElementPtr el, unsigned int index) { + if (el) { + const TokenList &x = el->Tokens(); + TokenPtr token = el->KeyToken(); + + ERR_FAIL_COND_V(!token, nullptr); + + if (index >= x.size()) { + ERR_FAIL_V_MSG(nullptr, "missing token at index: " + itos(index) + " " + String(token->StringContents().c_str())); + } + + return x[index]; + } + + return nullptr; +} + +// ------------------------------------------------------------------------------------------------ +// wrapper around ParseTokenAsDim() with print_error handling +size_t ParseTokenAsDim(const TokenPtr t) { + const char *err; + const size_t i = ParseTokenAsDim(t, err); + if (err) { + print_error(String(err) + " " + String(t->StringContents().c_str())); + } + return i; +} + +// ------------------------------------------------------------------------------------------------ +// wrapper around ParseTokenAsFloat() with print_error handling +float ParseTokenAsFloat(const TokenPtr t) { + const char *err; + const float i = ParseTokenAsFloat(t, err); + if (err) { + print_error(String(err) + " " + String(t->StringContents().c_str())); + } + return i; +} + +// ------------------------------------------------------------------------------------------------ +// wrapper around ParseTokenAsInt() with print_error handling +int ParseTokenAsInt(const TokenPtr t) { + const char *err; + const int i = ParseTokenAsInt(t, err); + if (err) { + print_error(String(err) + " " + String(t->StringContents().c_str())); + } + return i; +} + +// ------------------------------------------------------------------------------------------------ +// wrapper around ParseTokenAsInt64() with print_error handling +int64_t ParseTokenAsInt64(const TokenPtr t) { + const char *err; + const int64_t i = ParseTokenAsInt64(t, err); + if (err) { + print_error(String(err) + " " + String(t->StringContents().c_str())); + } + return i; +} +} // namespace FBXDocParser diff --git a/modules/fbx/fbx_parser/FBXParser.h b/modules/fbx/fbx_parser/FBXParser.h new file mode 100644 index 0000000000..37d27d3dca --- /dev/null +++ b/modules/fbx/fbx_parser/FBXParser.h @@ -0,0 +1,263 @@ +/*************************************************************************/ +/* FBXParser.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXParser.h + * @brief FBX parsing code + */ +#ifndef FBX_PARSER_H +#define FBX_PARSER_H + +#include <stdint.h> +#include <map> +#include <memory> + +#include "core/math/color.h" +#include "core/math/transform.h" +#include "core/math/vector2.h" +#include "core/math/vector3.h" + +#include "FBXTokenizer.h" + +namespace FBXDocParser { + +class Scope; +class Parser; +class Element; + +typedef Element *ElementPtr; +typedef Scope *ScopePtr; + +typedef std::vector<ScopePtr> ScopeList; +typedef std::multimap<std::string, ElementPtr> ElementMap; +typedef std::pair<ElementMap::const_iterator, ElementMap::const_iterator> ElementCollection; + +#define new_Scope new Scope +#define new_Element new Element + +/** FBX data entity that consists of a key:value tuple. + * + * Example: + * @verbatim + * AnimationCurve: 23, "AnimCurve::", "" { + * [..] + * } + * @endverbatim + * + * As can be seen in this sample, elements can contain nested #Scope + * as their trailing member. **/ +class Element { +public: + Element(TokenPtr key_token, Parser &parser); + ~Element(); + + ScopePtr Compound() const { + return compound; + } + + TokenPtr KeyToken() const { + return key_token; + } + + const TokenList &Tokens() const { + return tokens; + } + +private: + TokenList tokens; + ScopePtr compound = nullptr; + std::vector<ScopePtr> compound_scope; + TokenPtr key_token = nullptr; +}; + +/** FBX data entity that consists of a 'scope', a collection + * of not necessarily unique #Element instances. + * + * Example: + * @verbatim + * GlobalSettings: { + * Version: 1000 + * Properties70: + * [...] + * } + * @endverbatim */ +class Scope { +public: + Scope(Parser &parser, bool topLevel = false); + ~Scope(); + + ElementPtr GetElement(const std::string &index) const { + ElementMap::const_iterator it = elements.find(index); + return it == elements.end() ? nullptr : (*it).second; + } + + ElementPtr FindElementCaseInsensitive(const std::string &elementName) const { + for (auto element = elements.begin(); element != elements.end(); ++element) { + if (element->first.compare(elementName)) { + return element->second; + } + } + + // nothing to reference / expired. + return nullptr; + } + + ElementCollection GetCollection(const std::string &index) const { + return elements.equal_range(index); + } + + const ElementMap &Elements() const { + return elements; + } + +private: + ElementMap elements; +}; + +/** FBX parsing class, takes a list of input tokens and generates a hierarchy + * of nested #Scope instances, representing the fbx DOM.*/ +class Parser { +public: + /** Parse given a token list. Does not take ownership of the tokens - + * the objects must persist during the entire parser lifetime */ + Parser(const TokenList &tokens, bool is_binary); + ~Parser(); + + ScopePtr GetRootScope() const { + return root; + } + + bool IsBinary() const { + return is_binary; + } + +private: + friend class Scope; + friend class Element; + + TokenPtr AdvanceToNextToken(); + TokenPtr LastToken() const; + TokenPtr CurrentToken() const; + +private: + ScopeList scopes; + const TokenList &tokens; + + TokenPtr last = nullptr, current = nullptr; + TokenList::const_iterator cursor; + ScopePtr root = nullptr; + + const bool is_binary; +}; + +/* token parsing - this happens when building the DOM out of the parse-tree*/ +uint64_t ParseTokenAsID(const TokenPtr t, const char *&err_out); +size_t ParseTokenAsDim(const TokenPtr t, const char *&err_out); +float ParseTokenAsFloat(const TokenPtr t, const char *&err_out); +int ParseTokenAsInt(const TokenPtr t, const char *&err_out); +int64_t ParseTokenAsInt64(const TokenPtr t, const char *&err_out); +std::string ParseTokenAsString(const TokenPtr t, const char *&err_out); + +/* wrapper around ParseTokenAsXXX() with DOMError handling */ +uint64_t ParseTokenAsID(const TokenPtr t); +size_t ParseTokenAsDim(const TokenPtr t); +float ParseTokenAsFloat(const TokenPtr t); +int ParseTokenAsInt(const TokenPtr t); +int64_t ParseTokenAsInt64(const TokenPtr t); +std::string ParseTokenAsString(const TokenPtr t); + +/* read data arrays */ +void ParseVectorDataArray(std::vector<Vector3> &out, const ElementPtr el); +void ParseVectorDataArray(std::vector<Color> &out, const ElementPtr el); +void ParseVectorDataArray(std::vector<Vector2> &out, const ElementPtr el); +void ParseVectorDataArray(std::vector<int> &out, const ElementPtr el); +void ParseVectorDataArray(std::vector<float> &out, const ElementPtr el); +void ParseVectorDataArray(std::vector<float> &out, const ElementPtr el); +void ParseVectorDataArray(std::vector<unsigned int> &out, const ElementPtr el); +void ParseVectorDataArray(std::vector<uint64_t> &out, const ElementPtr ep); +void ParseVectorDataArray(std::vector<int64_t> &out, const ElementPtr el); +bool HasElement(const ScopePtr sc, const std::string &index); + +// extract a required element from a scope, abort if the element cannot be found +ElementPtr GetRequiredElement(const ScopePtr sc, const std::string &index, const ElementPtr element = nullptr); +ScopePtr GetRequiredScope(const ElementPtr el); // New in 2020. (less likely to destroy application) +ElementPtr GetOptionalElement(const ScopePtr sc, const std::string &index, const ElementPtr element = nullptr); +// extract required compound scope +ScopePtr GetRequiredScope(const ElementPtr el); +// get token at a particular index +TokenPtr GetRequiredToken(const ElementPtr el, unsigned int index); + +// ------------------------------------------------------------------------------------------------ +// read a 4x4 matrix from an array of 16 floats +Transform ReadMatrix(const ElementPtr element); +} // namespace FBXDocParser + +#endif // FBX_PARSER_H diff --git a/modules/fbx/fbx_parser/FBXPose.cpp b/modules/fbx/fbx_parser/FBXPose.cpp new file mode 100644 index 0000000000..6d80b85e38 --- /dev/null +++ b/modules/fbx/fbx_parser/FBXPose.cpp @@ -0,0 +1,104 @@ +/*************************************************************************/ +/* FBXPose.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXNoteAttribute.cpp + * @brief Assimp::FBX::NodeAttribute (and subclasses) implementation + */ + +#include "FBXDocument.h" +#include "FBXParser.h" +#include <iostream> + +namespace FBXDocParser { + +class FbxPoseNode; +// ------------------------------------------------------------------------------------------------ +FbxPose::FbxPose(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) : + Object(id, element, name) { + const ScopePtr sc = GetRequiredScope(element); + //const std::string &classname = ParseTokenAsString(GetRequiredToken(element, 2)); + + const ElementCollection &PoseNodes = sc->GetCollection("PoseNode"); + for (ElementMap::const_iterator it = PoseNodes.first; it != PoseNodes.second; ++it) { + std::string entry_name = (*it).first; + ElementPtr some_element = (*it).second; + FbxPoseNode *pose_node = new FbxPoseNode(some_element, doc, entry_name); + pose_nodes.push_back(pose_node); + } +} + +// ------------------------------------------------------------------------------------------------ +FbxPose::~FbxPose() { + pose_nodes.clear(); + // empty +} +} // namespace FBXDocParser diff --git a/modules/fbx/fbx_parser/FBXProperties.cpp b/modules/fbx/fbx_parser/FBXProperties.cpp new file mode 100644 index 0000000000..8ab94e1ef4 --- /dev/null +++ b/modules/fbx/fbx_parser/FBXProperties.cpp @@ -0,0 +1,237 @@ +/*************************************************************************/ +/* FBXProperties.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXProperties.cpp + * @brief Implementation of the FBX dynamic properties system + */ + +#include "FBXProperties.h" +#include "FBXDocumentUtil.h" +#include "FBXParser.h" +#include "FBXTokenizer.h" + +namespace FBXDocParser { + +using namespace Util; + +// ------------------------------------------------------------------------------------------------ +Property::Property() { +} + +// ------------------------------------------------------------------------------------------------ +Property::~Property() { +} + +namespace { + +// ------------------------------------------------------------------------------------------------ +// read a typed property out of a FBX element. The return value is NULL if the property cannot be read. +PropertyPtr ReadTypedProperty(const ElementPtr element) { + //ai_assert(element.KeyToken().StringContents() == "P"); + + const TokenList &tok = element->Tokens(); + //ai_assert(tok.size() >= 5); + + const std::string &s = ParseTokenAsString(tok[1]); + const char *const cs = s.c_str(); + if (!strcmp(cs, "KString")) { + return new TypedProperty<std::string>(ParseTokenAsString(tok[4])); + } else if (!strcmp(cs, "bool") || !strcmp(cs, "Bool")) { + return new TypedProperty<bool>(ParseTokenAsInt(tok[4]) != 0); + } else if (!strcmp(cs, "int") || !strcmp(cs, "Int") || !strcmp(cs, "enum") || !strcmp(cs, "Enum")) { + return new TypedProperty<int>(ParseTokenAsInt(tok[4])); + } else if (!strcmp(cs, "ULongLong")) { + return new TypedProperty<uint64_t>(ParseTokenAsID(tok[4])); + } else if (!strcmp(cs, "KTime")) { + return new TypedProperty<int64_t>(ParseTokenAsInt64(tok[4])); + } else if (!strcmp(cs, "Vector3D") || + !strcmp(cs, "ColorRGB") || + !strcmp(cs, "Vector") || + !strcmp(cs, "Color") || + !strcmp(cs, "Lcl Translation") || + !strcmp(cs, "Lcl Rotation") || + !strcmp(cs, "Lcl Scaling")) { + return new TypedProperty<Vector3>(Vector3( + ParseTokenAsFloat(tok[4]), + ParseTokenAsFloat(tok[5]), + ParseTokenAsFloat(tok[6]))); + } else if (!strcmp(cs, "double") || !strcmp(cs, "Number") || !strcmp(cs, "Float") || !strcmp(cs, "float") || !strcmp(cs, "FieldOfView") || !strcmp(cs, "UnitScaleFactor")) { + return new TypedProperty<float>(ParseTokenAsFloat(tok[4])); + } + + return nullptr; +} + +// ------------------------------------------------------------------------------------------------ +// peek into an element and check if it contains a FBX property, if so return its name. +std::string PeekPropertyName(const Element &element) { + //ai_assert(element.KeyToken().StringContents() == "P"); + const TokenList &tok = element.Tokens(); + if (tok.size() < 4) { + return ""; + } + + return ParseTokenAsString(tok[0]); +} +} // namespace + +// ------------------------------------------------------------------------------------------------ +PropertyTable::PropertyTable() : + templateProps(), element() { +} + +// ------------------------------------------------------------------------------------------------ +PropertyTable::PropertyTable(const ElementPtr element, const PropertyTable *templateProps) : + templateProps(templateProps), element(element) { + const ScopePtr scope = GetRequiredScope(element); + ERR_FAIL_COND(!scope); + for (const ElementMap::value_type &v : scope->Elements()) { + if (v.first != "P") { + DOMWarning("expected only P elements in property table", v.second); + continue; + } + + const std::string &name = PeekPropertyName(*v.second); + if (!name.length()) { + DOMWarning("could not read property name", v.second); + continue; + } + + LazyPropertyMap::const_iterator it = lazyProps.find(name); + if (it != lazyProps.end()) { + DOMWarning("duplicate property name, will hide previous value: " + name, v.second); + continue; + } + + // since the above checks for duplicates we can be sure to insert the only match here. + lazyProps[name] = v.second; + } +} + +// ------------------------------------------------------------------------------------------------ +PropertyTable::~PropertyTable() { + for (PropertyMap::value_type &v : props) { + delete v.second; + } +} + +// ------------------------------------------------------------------------------------------------ +PropertyPtr PropertyTable::Get(const std::string &name) const { + PropertyMap::const_iterator it = props.find(name); + if (it == props.end()) { + // hasn't been parsed yet? + LazyPropertyMap::const_iterator lit = lazyProps.find(name); + if (lit != lazyProps.end()) { + props[name] = ReadTypedProperty(lit->second); + it = props.find(name); + + //ai_assert(it != props.end()); + } + + if (it == props.end()) { + // check property template + if (templateProps) { + return templateProps->Get(name); + } + + return nullptr; + } + } + + return (*it).second; +} + +DirectPropertyMap PropertyTable::GetUnparsedProperties() const { + DirectPropertyMap result; + + // Loop through all the lazy properties (which is all the properties) + for (const LazyPropertyMap::value_type &element : lazyProps) { + // Skip parsed properties + if (props.end() != props.find(element.first)) + continue; + + // Read the element's value. + // Wrap the naked pointer (since the call site is required to acquire ownership) + // std::unique_ptr from C++11 would be preferred both as a wrapper and a return value. + Property *prop = ReadTypedProperty(element.second); + + // Element could not be read. Skip it. + if (!prop) + continue; + + // Add to result + result[element.first] = prop; + } + + return result; +} +} // namespace FBXDocParser diff --git a/modules/fbx/fbx_parser/FBXProperties.h b/modules/fbx/fbx_parser/FBXProperties.h new file mode 100644 index 0000000000..27cacfaf76 --- /dev/null +++ b/modules/fbx/fbx_parser/FBXProperties.h @@ -0,0 +1,221 @@ +/*************************************************************************/ +/* FBXProperties.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXProperties.h + * @brief FBX dynamic properties + */ +#ifndef FBX_PROPERTIES_H +#define FBX_PROPERTIES_H + +#include "FBXParser.h" +#include <map> +#include <memory> +#include <string> +#include <vector> + +namespace FBXDocParser { + +// Forward declarations +class Element; + +/** Represents a dynamic property. Type info added by deriving classes, + * see #TypedProperty. + Example: + @verbatim + P: "ShininessExponent", "double", "Number", "",0.5 + @endvebatim +*/ +class Property { +protected: + Property(); + +public: + virtual ~Property(); + +public: + template <typename T> + const T *As() const { + return dynamic_cast<const T *>(this); + } +}; + +template <typename T> +class TypedProperty : public Property { +public: + explicit TypedProperty(const T &value) : + value(value) { + // empty + } + + const T &Value() const { + return value; + } + +private: + T value; +}; + +#define new_Property new Property +typedef Property *PropertyPtr; +typedef std::map<std::string, PropertyPtr> DirectPropertyMap; +typedef std::map<std::string, PropertyPtr> PropertyMap; +typedef std::map<std::string, ElementPtr> LazyPropertyMap; + +/** + * Represents a property table as can be found in the newer FBX files (Properties60, Properties70) + */ +class PropertyTable { +public: + // in-memory property table with no source element + PropertyTable(); + PropertyTable(const ElementPtr element, const PropertyTable *templateProps); + ~PropertyTable(); + + PropertyPtr Get(const std::string &name) const; + + // PropertyTable's need not be coupled with FBX elements so this can be NULL + ElementPtr GetElement() const { + return element; + } + + PropertyMap &GetProperties() const { + return props; + } + + const LazyPropertyMap &GetLazyProperties() const { + return lazyProps; + } + + const PropertyTable *TemplateProps() const { + return templateProps; + } + + DirectPropertyMap GetUnparsedProperties() const; + +private: + LazyPropertyMap lazyProps; + mutable PropertyMap props; + const PropertyTable *templateProps = nullptr; + const ElementPtr element = nullptr; +}; + +// ------------------------------------------------------------------------------------------------ +template <typename T> +inline T PropertyGet(const PropertyTable *in, const std::string &name, const T &defaultValue) { + PropertyPtr prop = in->Get(name); + if (nullptr == prop) { + return defaultValue; + } + + // strong typing, no need to be lenient + const TypedProperty<T> *const tprop = prop->As<TypedProperty<T>>(); + if (nullptr == tprop) { + return defaultValue; + } + + return tprop->Value(); +} + +// ------------------------------------------------------------------------------------------------ +template <typename T> +inline T PropertyGet(const PropertyTable *in, const std::string &name, bool &result, bool useTemplate = false) { + PropertyPtr prop = in->Get(name); + if (nullptr == prop) { + if (!useTemplate) { + result = false; + return T(); + } + const PropertyTable *templ = in->TemplateProps(); + if (nullptr == templ) { + result = false; + return T(); + } + prop = templ->Get(name); + if (nullptr == prop) { + result = false; + return T(); + } + } + + // strong typing, no need to be lenient + const TypedProperty<T> *const tprop = prop->As<TypedProperty<T>>(); + if (nullptr == tprop) { + result = false; + return T(); + } + + result = true; + return tprop->Value(); +} +} // namespace FBXDocParser + +#endif // FBX_PROPERTIES_H diff --git a/modules/fbx/fbx_parser/FBXTokenizer.cpp b/modules/fbx/fbx_parser/FBXTokenizer.cpp new file mode 100644 index 0000000000..ea4568fe32 --- /dev/null +++ b/modules/fbx/fbx_parser/FBXTokenizer.cpp @@ -0,0 +1,251 @@ +/*************************************************************************/ +/* FBXTokenizer.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXTokenizer.cpp + * @brief Implementation of the FBX broadphase lexer + */ + +// tab width for logging columns +#define ASSIMP_FBX_TAB_WIDTH 4 + +#include "FBXTokenizer.h" +#include "core/string/print_string.h" + +namespace FBXDocParser { + +// ------------------------------------------------------------------------------------------------ +Token::Token(const char *p_sbegin, const char *p_send, TokenType p_type, unsigned int p_line, unsigned int p_column) : + sbegin(p_sbegin), + send(p_send), + type(p_type), + line(p_line), + column(p_column) { +#ifdef DEBUG_ENABLED + contents = std::string(sbegin, static_cast<size_t>(send - sbegin)); +#endif +} + +// ------------------------------------------------------------------------------------------------ +Token::~Token() { +} + +namespace { + +// ------------------------------------------------------------------------------------------------ +void TokenizeError(const std::string &message, unsigned int line, unsigned int column) { + print_error("[FBX-Tokenize]" + String(message.c_str()) + " " + itos(line) + ":" + itos(column)); +} + +// process a potential data token up to 'cur', adding it to 'output_tokens'. +// ------------------------------------------------------------------------------------------------ +void ProcessDataToken(TokenList &output_tokens, const char *&start, const char *&end, + unsigned int line, + unsigned int column, + TokenType type = TokenType_DATA, + bool must_have_token = false) { + if (start && end) { + // sanity check: + // tokens should have no whitespace outside quoted text and [start,end] should + // properly delimit the valid range. + bool in_double_quotes = false; + for (const char *c = start; c != end + 1; ++c) { + if (*c == '\"') { + in_double_quotes = !in_double_quotes; + } + + if (!in_double_quotes && IsSpaceOrNewLine(*c)) { + TokenizeError("unexpected whitespace in token", line, column); + } + } + + if (in_double_quotes) { + TokenizeError("non-terminated double quotes", line, column); + } + + output_tokens.push_back(new_Token(start, end + 1, type, line, column)); + } else if (must_have_token) { + TokenizeError("unexpected character, expected data token", line, column); + } + + start = end = nullptr; +} +} // namespace + +// ------------------------------------------------------------------------------------------------ +void Tokenize(TokenList &output_tokens, const char *input, size_t length) { + // line and column numbers numbers are one-based + unsigned int line = 1; + unsigned int column = 1; + + bool comment = false; + bool in_double_quotes = false; + bool pending_data_token = false; + + const char *token_begin = nullptr, *token_end = nullptr; + + // input (starting string), *cur the current string, column += + // modified to fix strlen() and stop buffer overflow + for (size_t x = 0; x < length; x++) { + const char c = input[x]; + const char *cur = &input[x]; + column += (c == '\t' ? ASSIMP_FBX_TAB_WIDTH : 1); + + if (IsLineEnd(c)) { + comment = false; + + column = 0; + ++line; + } + + if (comment) { + continue; + } + + if (in_double_quotes) { + if (c == '\"') { + in_double_quotes = false; + token_end = cur; + + ProcessDataToken(output_tokens, token_begin, token_end, line, column); + pending_data_token = false; + } + continue; + } + + switch (c) { + case '\"': + if (token_begin) { + TokenizeError("unexpected double-quote", line, column); + } + token_begin = cur; + in_double_quotes = true; + continue; + + case ';': + ProcessDataToken(output_tokens, token_begin, token_end, line, column); + comment = true; + continue; + + case '{': + ProcessDataToken(output_tokens, token_begin, token_end, line, column); + output_tokens.push_back(new_Token(cur, cur + 1, TokenType_OPEN_BRACKET, line, column)); + continue; + + case '}': + ProcessDataToken(output_tokens, token_begin, token_end, line, column); + output_tokens.push_back(new_Token(cur, cur + 1, TokenType_CLOSE_BRACKET, line, column)); + continue; + + case ',': + if (pending_data_token) { + ProcessDataToken(output_tokens, token_begin, token_end, line, column, TokenType_DATA, true); + } + output_tokens.push_back(new_Token(cur, cur + 1, TokenType_COMMA, line, column)); + continue; + + case ':': + if (pending_data_token) { + ProcessDataToken(output_tokens, token_begin, token_end, line, column, TokenType_KEY, true); + } else { + TokenizeError("unexpected colon", line, column); + } + continue; + } + + if (IsSpaceOrNewLine(c)) { + if (token_begin) { + // peek ahead and check if the next token is a colon in which + // case this counts as KEY token. + TokenType type = TokenType_DATA; + for (const char *peek = cur; *peek && IsSpaceOrNewLine(*peek); ++peek) { + if (*peek == ':') { + type = TokenType_KEY; + cur = peek; + break; + } + } + + ProcessDataToken(output_tokens, token_begin, token_end, line, column, type); + } + + pending_data_token = false; + } else { + token_end = cur; + if (!token_begin) { + token_begin = cur; + } + + pending_data_token = true; + } + } +} +} // namespace FBXDocParser diff --git a/modules/fbx/fbx_parser/FBXTokenizer.h b/modules/fbx/fbx_parser/FBXTokenizer.h new file mode 100644 index 0000000000..1e7e5e6535 --- /dev/null +++ b/modules/fbx/fbx_parser/FBXTokenizer.h @@ -0,0 +1,203 @@ +/*************************************************************************/ +/* FBXTokenizer.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXTokenizer.h + * @brief FBX lexer + */ +#ifndef FBX_TOKENIZER_H +#define FBX_TOKENIZER_H + +#include "FBXParseTools.h" +#include "core/string/ustring.h" +#include <iostream> +#include <memory> +#include <string> +#include <vector> + +namespace FBXDocParser { +/** Rough classification for text FBX tokens used for constructing the + * basic scope hierarchy. */ +enum TokenType { + // { + TokenType_OPEN_BRACKET = 0, + + // } + TokenType_CLOSE_BRACKET, + + // '"blablubb"', '2', '*14' - very general token class, + // further processing happens at a later stage. + TokenType_DATA, + + // + TokenType_BINARY_DATA, + + // , + TokenType_COMMA, + + // blubb: + TokenType_KEY +}; + +/** Represents a single token in a FBX file. Tokens are + * classified by the #TokenType enumerated types. + * + * Offers iterator protocol. Tokens are immutable. */ +class Token { +private: + static const unsigned int BINARY_MARKER = static_cast<unsigned int>(-1); + +public: + /** construct a textual token */ + Token(const char *p_sbegin, const char *p_send, TokenType p_type, unsigned int p_line, unsigned int p_column); + + /** construct a binary token */ + Token(const char *p_sbegin, const char *p_send, TokenType p_type, size_t p_offset); + ~Token(); + +public: + std::string StringContents() const { + return std::string(begin(), end()); + } + + bool IsBinary() const { + return column == BINARY_MARKER; + } + + const char *begin() const { + return sbegin; + } + + const char *end() const { + return send; + } + + TokenType Type() const { + return type; + } + + size_t Offset() const { + return offset; + } + + unsigned int Line() const { + return static_cast<unsigned int>(line); + } + + unsigned int Column() const { + return column; + } + +private: +#ifdef DEBUG_ENABLED + // full string copy for the sole purpose that it nicely appears + // in msvc's debugger window. + std::string contents; +#endif + + const char *sbegin = nullptr; + const char *send = nullptr; + const TokenType type; + + union { + size_t line; + size_t offset; + }; + const unsigned int column = 0; +}; + +// Fixed leak by using shared_ptr for tokens +typedef Token *TokenPtr; +typedef std::vector<TokenPtr> TokenList; + +#define new_Token new Token + +/** Main FBX tokenizer function. Transform input buffer into a list of preprocessed tokens. + * + * Skips over comments and generates line and column numbers. + * + * @param output_tokens Receives a list of all tokens in the input data. + * @param input_buffer Textual input buffer to be processed, 0-terminated. + * @print_error if something goes wrong */ +void Tokenize(TokenList &output_tokens, const char *input, size_t length); + +/** Tokenizer function for binary FBX files. + * + * Emits a token list suitable for direct parsing. + * + * @param output_tokens Receives a list of all tokens in the input data. + * @param input_buffer Binary input buffer to be processed. + * @param length Length of input buffer, in bytes. There is no 0-terminal. + * @print_error if something goes wrong */ +void TokenizeBinary(TokenList &output_tokens, const char *input, size_t length); +} // namespace FBXDocParser + +#endif // FBX_TOKENIZER_H diff --git a/modules/fbx/fbx_parser/FBXUtil.cpp b/modules/fbx/fbx_parser/FBXUtil.cpp new file mode 100644 index 0000000000..80ea5fab4c --- /dev/null +++ b/modules/fbx/fbx_parser/FBXUtil.cpp @@ -0,0 +1,220 @@ +/*************************************************************************/ +/* FBXUtil.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXUtil.cpp + * @brief Implementation of internal FBX utility functions + */ + +#include "FBXUtil.h" +#include "FBXTokenizer.h" +#include <cstring> +#include <string> + +namespace FBXDocParser { +namespace Util { + +// ------------------------------------------------------------------------------------------------ +const char *TokenTypeString(TokenType t) { + switch (t) { + case TokenType_OPEN_BRACKET: + return "TOK_OPEN_BRACKET"; + + case TokenType_CLOSE_BRACKET: + return "TOK_CLOSE_BRACKET"; + + case TokenType_DATA: + return "TOK_DATA"; + + case TokenType_COMMA: + return "TOK_COMMA"; + + case TokenType_KEY: + return "TOK_KEY"; + + case TokenType_BINARY_DATA: + return "TOK_BINARY_DATA"; + } + + //ai_assert(false); + return ""; +} + +// Generated by this formula: T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i; +static const uint8_t base64DecodeTable[128] = { + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 255, 255, 255, + 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, + 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255 +}; + +uint8_t DecodeBase64(char ch) { + const auto idx = static_cast<uint8_t>(ch); + if (idx > 127) + return 255; + return base64DecodeTable[idx]; +} + +size_t ComputeDecodedSizeBase64(const char *in, size_t inLength) { + if (inLength < 2) { + return 0; + } + const size_t equals = size_t(in[inLength - 1] == '=') + size_t(in[inLength - 2] == '='); + const size_t full_length = (inLength * 3) >> 2; // div by 4 + if (full_length < equals) { + return 0; + } + return full_length - equals; +} + +size_t DecodeBase64(const char *in, size_t inLength, uint8_t *out, size_t maxOutLength) { + if (maxOutLength == 0 || inLength < 2) { + return 0; + } + const size_t realLength = inLength - size_t(in[inLength - 1] == '=') - size_t(in[inLength - 2] == '='); + size_t dst_offset = 0; + int val = 0, valb = -8; + for (size_t src_offset = 0; src_offset < realLength; ++src_offset) { + const uint8_t table_value = Util::DecodeBase64(in[src_offset]); + if (table_value == 255) { + return 0; + } + val = (val << 6) + table_value; + valb += 6; + if (valb >= 0) { + out[dst_offset++] = static_cast<uint8_t>((val >> valb) & 0xFF); + valb -= 8; + val &= 0xFFF; + } + } + return dst_offset; +} + +static const char to_base64_string[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +char EncodeBase64(char byte) { + return to_base64_string[(size_t)byte]; +} + +/** Encodes a block of 4 bytes to base64 encoding +* @param bytes Bytes to encode. +* @param out_string String to write encoded values to. +* @param string_pos Position in out_string. +*/ +void EncodeByteBlock(const char *bytes, std::string &out_string, size_t string_pos) { + char b0 = (bytes[0] & 0xFC) >> 2; + char b1 = (bytes[0] & 0x03) << 4 | ((bytes[1] & 0xF0) >> 4); + char b2 = (bytes[1] & 0x0F) << 2 | ((bytes[2] & 0xC0) >> 6); + char b3 = (bytes[2] & 0x3F); + + out_string[string_pos + 0] = EncodeBase64(b0); + out_string[string_pos + 1] = EncodeBase64(b1); + out_string[string_pos + 2] = EncodeBase64(b2); + out_string[string_pos + 3] = EncodeBase64(b3); +} + +std::string EncodeBase64(const char *data, size_t length) { + // calculate extra bytes needed to get a multiple of 3 + size_t extraBytes = 3 - length % 3; + + // number of base64 bytes + size_t encodedBytes = 4 * (length + extraBytes) / 3; + + std::string encoded_string(encodedBytes, '='); + + // read blocks of 3 bytes + for (size_t ib3 = 0; ib3 < length / 3; ib3++) { + const size_t iByte = ib3 * 3; + const size_t iEncodedByte = ib3 * 4; + const char *currData = &data[iByte]; + + EncodeByteBlock(currData, encoded_string, iEncodedByte); + } + + // if size of data is not a multiple of 3, also encode the final bytes (and add zeros where needed) + if (extraBytes > 0) { + char finalBytes[4] = { 0, 0, 0, 0 }; + memcpy(&finalBytes[0], &data[length - length % 3], length % 3); + + const size_t iEncodedByte = encodedBytes - 4; + EncodeByteBlock(&finalBytes[0], encoded_string, iEncodedByte); + + // add '=' at the end + for (size_t i = 0; i < 4 * extraBytes / 3; i++) + encoded_string[encodedBytes - i - 1] = '='; + } + return encoded_string; +} +} // namespace Util +} // namespace FBXDocParser diff --git a/modules/fbx/fbx_parser/FBXUtil.h b/modules/fbx/fbx_parser/FBXUtil.h new file mode 100644 index 0000000000..efc131831b --- /dev/null +++ b/modules/fbx/fbx_parser/FBXUtil.h @@ -0,0 +1,122 @@ +/*************************************************************************/ +/* FBXUtil.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXUtil.h + * @brief FBX utility functions for internal use + */ +#ifndef FBX_UTIL_H +#define FBX_UTIL_H + +#include "FBXTokenizer.h" +#include <stdint.h> + +namespace FBXDocParser { + +namespace Util { + +/** Get a string representation for a #TokenType. */ +const char *TokenTypeString(TokenType t); + +/** Decode a single Base64-encoded character. +* +* @param ch Character to decode (from base64 to binary). +* @return decoded byte value*/ +uint8_t DecodeBase64(char ch); + +/** Compute decoded size of a Base64-encoded string +* +* @param in Characters to decode. +* @param inLength Number of characters to decode. +* @return size of the decoded data (number of bytes)*/ +size_t ComputeDecodedSizeBase64(const char *in, size_t inLength); + +/** Decode a Base64-encoded string +* +* @param in Characters to decode. +* @param inLength Number of characters to decode. +* @param out Pointer where we will store the decoded data. +* @param maxOutLength Size of output buffer. +* @return size of the decoded data (number of bytes)*/ +size_t DecodeBase64(const char *in, size_t inLength, uint8_t *out, size_t maxOutLength); + +char EncodeBase64(char byte); + +/** Encode bytes in base64-encoding +* +* @param data Binary data to encode. +* @param inLength Number of bytes to encode. +* @return base64-encoded string*/ +std::string EncodeBase64(const char *data, size_t length); +} // namespace Util +} // namespace FBXDocParser + +#endif // FBX_UTIL_H diff --git a/modules/fbx/fbx_parser/LICENSE b/modules/fbx/fbx_parser/LICENSE new file mode 100644 index 0000000000..b42fc6efe6 --- /dev/null +++ b/modules/fbx/fbx_parser/LICENSE @@ -0,0 +1,39 @@ +The files in this folder were originally from ASSIMP, but have been heavily modified to fix bugs and match coding +conventions of the Godot Engine project. We have kept a copy of the applicable licenses in the folder as required by +the license. + +Open Asset Import Library (assimp) + +Copyright (c) 2006-2020, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/modules/assimp/register_types.cpp b/modules/fbx/register_types.cpp index 6cb0fc982f..c0591dbc77 100644 --- a/modules/assimp/register_types.cpp +++ b/modules/fbx/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,22 +31,22 @@ #include "register_types.h" #include "editor/editor_node.h" -#include "editor_scene_importer_assimp.h" +#include "editor_scene_importer_fbx.h" #ifdef TOOLS_ENABLED static void _editor_init() { - Ref<EditorSceneImporterAssimp> import_assimp; - import_assimp.instance(); - ResourceImporterScene::get_singleton()->add_importer(import_assimp); + Ref<EditorSceneImporterFBX> import_fbx; + import_fbx.instance(); + ResourceImporterScene::get_singleton()->add_importer(import_fbx); } #endif -void register_assimp_types() { +void register_fbx_types() { #ifdef TOOLS_ENABLED ClassDB::APIType prev_api = ClassDB::get_current_api(); ClassDB::set_current_api(ClassDB::API_EDITOR); - ClassDB::register_class<EditorSceneImporterAssimp>(); + ClassDB::register_class<EditorSceneImporterFBX>(); ClassDB::set_current_api(prev_api); @@ -54,5 +54,5 @@ void register_assimp_types() { #endif } -void unregister_assimp_types() { +void unregister_fbx_types() { } diff --git a/modules/assimp/register_types.h b/modules/fbx/register_types.h index f399a7acc6..e5741afd72 100644 --- a/modules/assimp/register_types.h +++ b/modules/fbx/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,10 +28,10 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef ASSIMP_REGISTER_TYPES_H -#define ASSIMP_REGISTER_TYPES_H +#ifndef FBX_REGISTER_TYPES_H +#define FBX_REGISTER_TYPES_H -void register_assimp_types(); -void unregister_assimp_types(); +void register_fbx_types(); +void unregister_fbx_types(); -#endif // ASSIMP_REGISTER_TYPES_H +#endif // FBX_REGISTER_TYPES_H diff --git a/modules/fbx/tools/import_utils.cpp b/modules/fbx/tools/import_utils.cpp new file mode 100644 index 0000000000..c87dd1fd3a --- /dev/null +++ b/modules/fbx/tools/import_utils.cpp @@ -0,0 +1,151 @@ +/*************************************************************************/ +/* import_utils.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "import_utils.h" + +Vector3 ImportUtils::deg2rad(const Vector3 &p_rotation) { + return p_rotation / 180.0 * Math_PI; +} + +Vector3 ImportUtils::rad2deg(const Vector3 &p_rotation) { + return p_rotation / Math_PI * 180.0; +} + +Basis ImportUtils::EulerToBasis(FBXDocParser::Model::RotOrder mode, const Vector3 &p_rotation) { + Basis ret; + + // FBX is using intrinsic euler, we can convert intrinsic to extrinsic (the one used in godot + // by simply invert its order: https://www.cs.utexas.edu/~theshark/courses/cs354/lectures/cs354-14.pdf + switch (mode) { + case FBXDocParser::Model::RotOrder_EulerXYZ: + ret.set_euler_zyx(p_rotation); + break; + + case FBXDocParser::Model::RotOrder_EulerXZY: + ret.set_euler_yzx(p_rotation); + break; + + case FBXDocParser::Model::RotOrder_EulerYZX: + ret.set_euler_xzy(p_rotation); + break; + + case FBXDocParser::Model::RotOrder_EulerYXZ: + ret.set_euler_zxy(p_rotation); + break; + + case FBXDocParser::Model::RotOrder_EulerZXY: + ret.set_euler_yxz(p_rotation); + break; + + case FBXDocParser::Model::RotOrder_EulerZYX: + ret.set_euler_xyz(p_rotation); + break; + + case FBXDocParser::Model::RotOrder_SphericXYZ: + // TODO do this. + break; + + default: + // If you land here, Please integrate all enums. + CRASH_NOW_MSG("This is not unreachable."); + } + + return ret; +} + +Quat ImportUtils::EulerToQuaternion(FBXDocParser::Model::RotOrder mode, const Vector3 &p_rotation) { + return ImportUtils::EulerToBasis(mode, p_rotation); +} + +Vector3 ImportUtils::BasisToEuler(FBXDocParser::Model::RotOrder mode, const Basis &p_rotation) { + // FBX is using intrinsic euler, we can convert intrinsic to extrinsic (the one used in godot + // by simply invert its order: https://www.cs.utexas.edu/~theshark/courses/cs354/lectures/cs354-14.pdf + switch (mode) { + case FBXDocParser::Model::RotOrder_EulerXYZ: + return p_rotation.get_euler_zyx(); + + case FBXDocParser::Model::RotOrder_EulerXZY: + return p_rotation.get_euler_yzx(); + + case FBXDocParser::Model::RotOrder_EulerYZX: + return p_rotation.get_euler_xzy(); + + case FBXDocParser::Model::RotOrder_EulerYXZ: + return p_rotation.get_euler_zxy(); + + case FBXDocParser::Model::RotOrder_EulerZXY: + return p_rotation.get_euler_yxz(); + + case FBXDocParser::Model::RotOrder_EulerZYX: + return p_rotation.get_euler_xyz(); + + case FBXDocParser::Model::RotOrder_SphericXYZ: + // TODO + return Vector3(); + + default: + // If you land here, Please integrate all enums. + CRASH_NOW_MSG("This is not unreachable."); + return Vector3(); + } +} + +Vector3 ImportUtils::QuaternionToEuler(FBXDocParser::Model::RotOrder mode, const Quat &p_rotation) { + return BasisToEuler(mode, p_rotation); +} + +Transform get_unscaled_transform(const Transform &p_initial, real_t p_scale) { + Transform unscaled = Transform(p_initial.basis, p_initial.origin * p_scale); + ERR_FAIL_COND_V_MSG(unscaled.basis.determinant() == 0, Transform(), "det is zero unscaled?"); + return unscaled; +} + +Vector3 get_poly_normal(const std::vector<Vector3> &p_vertices) { + ERR_FAIL_COND_V_MSG(p_vertices.size() < 3, Vector3(0, 0, 0), "At least 3 vertices are necesary"); + // Using long double to make sure that normal is computed for even really tiny objects. + typedef long double ldouble; + ldouble x = 0.0; + ldouble y = 0.0; + ldouble z = 0.0; + for (size_t i = 0; i < p_vertices.size(); i += 1) { + const Vector3 current = p_vertices[i]; + const Vector3 next = p_vertices[(i + 1) % p_vertices.size()]; + x += (ldouble(current.y) - ldouble(next.y)) * (ldouble(current.z) + ldouble(next.z)); + y += (ldouble(current.z) - ldouble(next.z)) * (ldouble(current.x) + ldouble(next.x)); + z += (ldouble(current.x) - ldouble(next.x)) * (ldouble(current.y) + ldouble(next.y)); + } + const ldouble l2 = x * x + y * y + z * z; + if (l2 == 0.0) { + return (p_vertices[0] - p_vertices[1]).normalized().cross((p_vertices[0] - p_vertices[2]).normalized()).normalized(); + } else { + const double l = Math::sqrt(double(l2)); + return Vector3(x / l, y / l, z / l); + } +} diff --git a/modules/fbx/tools/import_utils.h b/modules/fbx/tools/import_utils.h new file mode 100644 index 0000000000..6261138812 --- /dev/null +++ b/modules/fbx/tools/import_utils.h @@ -0,0 +1,400 @@ +/*************************************************************************/ +/* import_utils.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef IMPORT_UTILS_FBX_IMPORTER_H +#define IMPORT_UTILS_FBX_IMPORTER_H + +#include "core/io/image_loader.h" + +#include "data/import_state.h" +#include "fbx_parser/FBXDocument.h" + +#include <string> + +#define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000LL + +/** + * Import Utils + * Conversion tools / glue code to convert from FBX to Godot +*/ +class ImportUtils { +public: + /// Convert a vector from degrees to radians. + static Vector3 deg2rad(const Vector3 &p_rotation); + + /// Convert a vector from radians to degrees. + static Vector3 rad2deg(const Vector3 &p_rotation); + + /// Converts rotation order vector (in rad) to quaternion. + static Basis EulerToBasis(FBXDocParser::Model::RotOrder mode, const Vector3 &p_rotation); + + /// Converts rotation order vector (in rad) to quaternion. + static Quat EulerToQuaternion(FBXDocParser::Model::RotOrder mode, const Vector3 &p_rotation); + + /// Converts basis into rotation order vector (in rad). + static Vector3 BasisToEuler(FBXDocParser::Model::RotOrder mode, const Basis &p_rotation); + + /// Converts quaternion into rotation order vector (in rad). + static Vector3 QuaternionToEuler(FBXDocParser::Model::RotOrder mode, const Quat &p_rotation); + + static void debug_xform(String name, const Transform &t) { + print_verbose(name + " " + t.origin + " rotation: " + (t.basis.get_euler() * (180 / Math_PI))); + } + + static String FBXNodeToName(const std::string &name) { + // strip Model:: prefix, avoiding ambiguities (i.e. don't strip if + // this causes ambiguities, well possible between empty identifiers, + // such as "Model::" and ""). Make sure the behaviour is consistent + // across multiple calls to FixNodeName(). + + // We must remove this from the name + // Some bones have this + // SubDeformer:: + // Meshes, Joints have this, some other IK elements too. + // Model:: + + String node_name = String(name.c_str()); + + if (node_name.substr(0, 7) == "Model::") { + node_name = node_name.substr(7, node_name.length() - 7); + return node_name.replace(":", ""); + } + + if (node_name.substr(0, 13) == "SubDeformer::") { + node_name = node_name.substr(13, node_name.length() - 13); + return node_name.replace(":", ""); + } + + if (node_name.substr(0, 11) == "AnimStack::") { + node_name = node_name.substr(11, node_name.length() - 11); + return node_name.replace(":", ""); + } + + if (node_name.substr(0, 15) == "AnimCurveNode::") { + node_name = node_name.substr(15, node_name.length() - 15); + return node_name.replace(":", ""); + } + + if (node_name.substr(0, 11) == "AnimCurve::") { + node_name = node_name.substr(11, node_name.length() - 11); + return node_name.replace(":", ""); + } + + if (node_name.substr(0, 10) == "Geometry::") { + node_name = node_name.substr(10, node_name.length() - 10); + return node_name.replace(":", ""); + } + + if (node_name.substr(0, 10) == "Material::") { + node_name = node_name.substr(10, node_name.length() - 10); + return node_name.replace(":", ""); + } + + if (node_name.substr(0, 9) == "Texture::") { + node_name = node_name.substr(9, node_name.length() - 9); + return node_name.replace(":", ""); + } + + return node_name.replace(":", ""); + } + + static std::string FBXAnimMeshName(const std::string &name) { + if (name.length()) { + size_t indexOf = name.find_first_of("::"); + if (indexOf != std::string::npos && indexOf < name.size() - 2) { + return name.substr(indexOf + 2); + } + } + return name.length() ? name : "AnimMesh"; + } + + static Vector3 safe_import_vector3(const Vector3 &p_vec) { + Vector3 vector = p_vec; + if (Math::is_equal_approx(0, vector.x)) { + vector.x = 0; + } + + if (Math::is_equal_approx(0, vector.y)) { + vector.y = 0; + } + + if (Math::is_equal_approx(0, vector.z)) { + vector.z = 0; + } + return vector; + } + + static void debug_xform(String name, const Basis &t) { + //print_verbose(name + " rotation: " + (t.get_euler() * (180 / Math_PI))); + } + + static Vector3 FixAxisConversions(Vector3 input) { + return Vector3(input.x, input.y, input.z); + } + + static void AlignMeshAxes(std::vector<Vector3> &vertex_data) { + for (size_t x = 0; x < vertex_data.size(); x++) { + vertex_data[x] = FixAxisConversions(vertex_data[x]); + } + } + + struct AssetImportFbx { + enum ETimeMode { + TIME_MODE_DEFAULT = 0, + TIME_MODE_120 = 1, + TIME_MODE_100 = 2, + TIME_MODE_60 = 3, + TIME_MODE_50 = 4, + TIME_MODE_48 = 5, + TIME_MODE_30 = 6, + TIME_MODE_30_DROP = 7, + TIME_MODE_NTSC_DROP_FRAME = 8, + TIME_MODE_NTSC_FULL_FRAME = 9, + TIME_MODE_PAL = 10, + TIME_MODE_CINEMA = 11, + TIME_MODE_1000 = 12, + TIME_MODE_CINEMA_ND = 13, + TIME_MODE_CUSTOM = 14, + TIME_MODE_TIME_MODE_COUNT = 15 + }; + enum UpAxis { + UP_VECTOR_AXIS_X = 1, + UP_VECTOR_AXIS_Y = 2, + UP_VECTOR_AXIS_Z = 3 + }; + enum FrontAxis { + FRONT_PARITY_EVEN = 1, + FRONT_PARITY_ODD = 2, + }; + + enum CoordAxis { + COORD_RIGHT = 0, + COORD_LEFT = 1 + }; + }; + + /** Get fbx fps for time mode meta data + */ + static float get_fbx_fps(int32_t time_mode) { + switch (time_mode) { + case AssetImportFbx::TIME_MODE_DEFAULT: + return 24; + case AssetImportFbx::TIME_MODE_120: + return 120; + case AssetImportFbx::TIME_MODE_100: + return 100; + case AssetImportFbx::TIME_MODE_60: + return 60; + case AssetImportFbx::TIME_MODE_50: + return 50; + case AssetImportFbx::TIME_MODE_48: + return 48; + case AssetImportFbx::TIME_MODE_30: + return 30; + case AssetImportFbx::TIME_MODE_30_DROP: + return 30; + case AssetImportFbx::TIME_MODE_NTSC_DROP_FRAME: + return 29.9700262f; + case AssetImportFbx::TIME_MODE_NTSC_FULL_FRAME: + return 29.9700262f; + case AssetImportFbx::TIME_MODE_PAL: + return 25; + case AssetImportFbx::TIME_MODE_CINEMA: + return 24; + case AssetImportFbx::TIME_MODE_1000: + return 1000; + case AssetImportFbx::TIME_MODE_CINEMA_ND: + return 23.976f; + case AssetImportFbx::TIME_MODE_CUSTOM: + return -1; + } + return 0; + } + + static float get_fbx_fps(const FBXDocParser::FileGlobalSettings *FBXSettings) { + int time_mode = FBXSettings->TimeMode(); + + // get the animation FPS + float frames_per_second = get_fbx_fps(time_mode); + + // handle animation custom FPS time. + if (time_mode == ImportUtils::AssetImportFbx::TIME_MODE_CUSTOM) { + print_verbose("FBX Animation has custom FPS setting"); + frames_per_second = FBXSettings->CustomFrameRate(); + + // not our problem this is the modeller, we can print as an error so they can fix the source. + if (frames_per_second == 0) { + print_error("Custom animation time in file is set to 0 value, animation won't play, please edit your file to correct the FPS value"); + } + } + return frames_per_second; + } + + /** + * Find hardcoded textures from assimp which could be in many different directories + */ + + /** + * set_texture_mapping_mode + * Helper to check the mapping mode of the texture (repeat, clamp and mirror) + */ + // static void set_texture_mapping_mode(aiTextureMapMode *map_mode, Ref<ImageTexture> texture) { + // ERR_FAIL_COND(texture.is_null()); + // ERR_FAIL_COND(map_mode == NULL); + // aiTextureMapMode tex_mode = map_mode[0]; + + // int32_t flags = Texture::FLAGS_DEFAULT; + // if (tex_mode == aiTextureMapMode_Wrap) { + // //Default + // } else if (tex_mode == aiTextureMapMode_Clamp) { + // flags = flags & ~Texture::FLAG_REPEAT; + // } else if (tex_mode == aiTextureMapMode_Mirror) { + // flags = flags | Texture::FLAG_MIRRORED_REPEAT; + // } + // texture->set_flags(flags); + // } + + /** + * Load or load from cache image :) + * We need to upgrade this in the later version :) should not be hard + */ + //static Ref<Image> load_image(ImportState &state, const aiScene *p_scene, String p_path){ + // Map<String, Ref<Image> >::Element *match = state.path_to_image_cache.find(p_path); + + // // if our cache contains this image then don't bother + // if (match) { + // return match->get(); + // } + + // Vector<String> split_path = p_path.get_basename().split("*"); + // if (split_path.size() == 2) { + // size_t texture_idx = split_path[1].to_int(); + // ERR_FAIL_COND_V(texture_idx >= p_scene->mNumTextures, Ref<Image>()); + // aiTexture *tex = p_scene->mTextures[texture_idx]; + // String filename = AssimpUtils::get_raw_string_from_assimp(tex->mFilename); + // filename = filename.get_file(); + // print_verbose("Open Asset Import: Loading embedded texture " + filename); + // if (tex->mHeight == 0) { + // if (tex->CheckFormat("png")) { + // Ref<Image> img = Image::_png_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth); + // ERR_FAIL_COND_V(img.is_null(), Ref<Image>()); + // state.path_to_image_cache.insert(p_path, img); + // return img; + // } else if (tex->CheckFormat("jpg")) { + // Ref<Image> img = Image::_jpg_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth); + // ERR_FAIL_COND_V(img.is_null(), Ref<Image>()); + // state.path_to_image_cache.insert(p_path, img); + // return img; + // } else if (tex->CheckFormat("dds")) { + // ERR_FAIL_COND_V_MSG(true, Ref<Image>(), "Open Asset Import: Embedded dds not implemented"); + // } + // } else { + // Ref<Image> img; + // img.instance(); + // PoolByteArray arr; + // uint32_t size = tex->mWidth * tex->mHeight; + // arr.resize(size); + // memcpy(arr.write().ptr(), tex->pcData, size); + // ERR_FAIL_COND_V(arr.size() % 4 != 0, Ref<Image>()); + // //ARGB8888 to RGBA8888 + // for (int32_t i = 0; i < arr.size() / 4; i++) { + // arr.write().ptr()[(4 * i) + 3] = arr[(4 * i) + 0]; + // arr.write().ptr()[(4 * i) + 0] = arr[(4 * i) + 1]; + // arr.write().ptr()[(4 * i) + 1] = arr[(4 * i) + 2]; + // arr.write().ptr()[(4 * i) + 2] = arr[(4 * i) + 3]; + // } + // img->create(tex->mWidth, tex->mHeight, true, Image::FORMAT_RGBA8, arr); + // ERR_FAIL_COND_V(img.is_null(), Ref<Image>()); + // state.path_to_image_cache.insert(p_path, img); + // return img; + // } + // return Ref<Image>(); + // } else { + // Ref<Texture> texture = ResourceLoader::load(p_path); + // ERR_FAIL_COND_V(texture.is_null(), Ref<Image>()); + // Ref<Image> image = texture->get_data(); + // ERR_FAIL_COND_V(image.is_null(), Ref<Image>()); + // state.path_to_image_cache.insert(p_path, image); + // return image; + // } + + // return Ref<Image>(); + //} + + // /* create texture from assimp data, if found in path */ + // static bool CreateAssimpTexture( + // AssimpImporter::ImportState &state, + // aiString texture_path, + // String &filename, + // String &path, + // AssimpImageData &image_state) { + // filename = get_raw_string_from_assimp(texture_path); + // path = state.path.get_base_dir().plus_file(filename.replace("\\", "/")); + // bool found = false; + // find_texture_path(state.path, path, found); + // if (found) { + // image_state.raw_image = AssimpUtils::load_image(state, state.assimp_scene, path); + // if (image_state.raw_image.is_valid()) { + // image_state.texture.instance(); + // image_state.texture->create_from_image(image_state.raw_image); + // image_state.texture->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY); + // return true; + // } + // } + + // return false; + // } + // /** GetAssimpTexture + // * Designed to retrieve textures for you + // */ + // static bool GetAssimpTexture( + // AssimpImporter::ImportState &state, + // aiMaterial *ai_material, + // aiTextureType texture_type, + // String &filename, + // String &path, + // AssimpImageData &image_state) { + // aiString ai_filename = aiString(); + // if (AI_SUCCESS == ai_material->GetTexture(texture_type, 0, &ai_filename, NULL, NULL, NULL, NULL, image_state.map_mode)) { + // return CreateAssimpTexture(state, ai_filename, filename, path, image_state); + // } + + // return false; + // } +}; + +// Apply the transforms so the basis will have scale 1. +Transform get_unscaled_transform(const Transform &p_initial, real_t p_scale); + +/// Uses the Newell's method to compute any polygon normal. +/// The polygon must be at least size of 3 or bigger. +Vector3 get_poly_normal(const std::vector<Vector3> &p_vertices); + +#endif // IMPORT_UTILS_FBX_IMPORTER_H diff --git a/modules/fbx/tools/validation_tools.cpp b/modules/fbx/tools/validation_tools.cpp new file mode 100644 index 0000000000..9dbd8bf544 --- /dev/null +++ b/modules/fbx/tools/validation_tools.cpp @@ -0,0 +1,48 @@ +/*************************************************************************/ +/* validation_tools.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "validation_tools.h" + +#ifdef TOOLS_ENABLED + +#include "core/string/print_string.h" +#include "core/string/ustring.h" + +ValidationTracker::Entries *ValidationTracker::entries_singleton = memnew(ValidationTracker::Entries); + +// for printing our CSV to dump validation problems of files +// later we can make some agnostic tooling for this but this is fine for the time being. +void ValidationTracker::Entries::add_validation_error(String asset_path, String message) { + print_error(message); + // note: implementation is static + validation_entries[asset_path].push_back(message); +} + +#endif // TOOLS_ENABLED diff --git a/modules/fbx/tools/validation_tools.h b/modules/fbx/tools/validation_tools.h new file mode 100644 index 0000000000..ced100aed2 --- /dev/null +++ b/modules/fbx/tools/validation_tools.h @@ -0,0 +1,92 @@ +/*************************************************************************/ +/* validation_tools.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef FBX_VALIDATION_TOOLS_H +#define FBX_VALIDATION_TOOLS_H + +#ifdef TOOLS_ENABLED + +#include "core/io/json.h" +#include "core/os/file_access.h" +#include "core/string/ustring.h" +#include "core/templates/local_vector.h" +#include "core/templates/map.h" + +class ValidationTracker { +protected: + struct Entries { + Map<String, LocalVector<String>> validation_entries = Map<String, LocalVector<String>>(); + + // for printing our CSV to dump validation problems of files + // later we can make some agnostic tooling for this but this is fine for the time being. + void add_validation_error(String asset_path, String message); + void print_to_csv() { + print_verbose("Exporting assset validation log please wait"); + String massive_log_file; + + String csv_header = "file_path, error message, extra data\n"; + massive_log_file += csv_header; + + for (Map<String, LocalVector<String>>::Element *element = validation_entries.front(); element; element = element->next()) { + for (unsigned int x = 0; x < element->value().size(); x++) { + const String &line_entry = element->key() + ", " + element->value()[x].c_escape() + "\n"; + massive_log_file += line_entry; + } + } + + String path = "asset_validation_errors.csv"; + Error err; + FileAccess *file = FileAccess::open(path, FileAccess::WRITE, &err); + if (!file || err) { + if (file) + memdelete(file); + print_error("ValidationTracker Error - failed to create file - path: %s\n" + path); + return; + } + + file->store_string(massive_log_file); + if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) { + print_error("ValidationTracker Error - failed to write to file - path: %s\n" + path); + } + file->close(); + memdelete(file); + } + }; + // asset path, error messages + static Entries *entries_singleton; + +public: + static Entries *get_singleton() { + return entries_singleton; + } +}; + +#endif // TOOLS_ENABLED +#endif // FBX_VALIDATION_TOOLS_H diff --git a/modules/freetype/SCsub b/modules/freetype/SCsub index bfc1658bb4..fc2535a6ca 100644 --- a/modules/freetype/SCsub +++ b/modules/freetype/SCsub @@ -6,6 +6,9 @@ Import("env_modules") env_freetype = env_modules.Clone() # Thirdparty source files + +thirdparty_obj = [] + if env["builtin_freetype"]: thirdparty_dir = "#thirdparty/freetype/" thirdparty_sources = [ @@ -84,6 +87,7 @@ if env["builtin_freetype"]: env_thirdparty = env_freetype.Clone() env_thirdparty.disable_warnings() lib = env_thirdparty.add_library("freetype_builtin", thirdparty_sources) + thirdparty_obj += lib # Needs to be appended to arrive after libscene in the linker call, # but we don't want it to arrive *after* system libs, so manual hack @@ -98,5 +102,13 @@ if env["builtin_freetype"]: if not inserted: env.Append(LIBS=[lib]) + # Godot source files -env_freetype.add_source_files(env.modules_sources, "*.cpp") + +module_obj = [] + +env_freetype.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj + +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/freetype/register_types.cpp b/modules/freetype/register_types.cpp index 15fa193183..e4e6a4c146 100644 --- a/modules/freetype/register_types.cpp +++ b/modules/freetype/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/freetype/register_types.h b/modules/freetype/register_types.h index aa8088d2e8..7a4f64b54b 100644 --- a/modules/freetype/register_types.h +++ b/modules/freetype/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/freetype/uwpdef.h b/modules/freetype/uwpdef.h index 2f9b2f4a53..f829edea67 100644 --- a/modules/freetype/uwpdef.h +++ b/modules/freetype/uwpdef.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gamecenter/game_center.h b/modules/gamecenter/game_center.h index 76fd295460..1ac00ca126 100644 --- a/modules/gamecenter/game_center.h +++ b/modules/gamecenter/game_center.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gamecenter/game_center.mm b/modules/gamecenter/game_center.mm index 114f639a32..b971bc1da3 100644 --- a/modules/gamecenter/game_center.mm +++ b/modules/gamecenter/game_center.mm @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gamecenter/game_center_delegate.h b/modules/gamecenter/game_center_delegate.h index 1b7025f915..ef1d2ae93d 100644 --- a/modules/gamecenter/game_center_delegate.h +++ b/modules/gamecenter/game_center_delegate.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gamecenter/game_center_delegate.mm b/modules/gamecenter/game_center_delegate.mm index 9a10c439c6..6e20db572b 100644 --- a/modules/gamecenter/game_center_delegate.mm +++ b/modules/gamecenter/game_center_delegate.mm @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gamecenter/game_center_module.cpp b/modules/gamecenter/game_center_module.cpp index 6c5157345f..8f6ef291c0 100644 --- a/modules/gamecenter/game_center_module.cpp +++ b/modules/gamecenter/game_center_module.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gamecenter/game_center_module.h b/modules/gamecenter/game_center_module.h index 8da3ae02ee..5df3645b1c 100644 --- a/modules/gamecenter/game_center_module.h +++ b/modules/gamecenter/game_center_module.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/android/android_gdn.cpp b/modules/gdnative/android/android_gdn.cpp index bc39be1813..a48e51a390 100644 --- a/modules/gdnative/android/android_gdn.cpp +++ b/modules/gdnative/android/android_gdn.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp index f397ee96c5..e3a359e09a 100644 --- a/modules/gdnative/gdnative.cpp +++ b/modules/gdnative/gdnative.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -286,7 +286,7 @@ bool GDNative::initialize() { } String lib_path = library->get_current_library_path(); - if (lib_path.empty()) { + if (lib_path.is_empty()) { ERR_PRINT("No library set for this platform"); return false; } diff --git a/modules/gdnative/gdnative.h b/modules/gdnative/gdnative.h index bba2c04a2a..765087d176 100644 --- a/modules/gdnative/gdnative.h +++ b/modules/gdnative/gdnative.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/gdnative/aabb.cpp b/modules/gdnative/gdnative/aabb.cpp index dc1b79b9e5..41b5029ef4 100644 --- a/modules/gdnative/gdnative/aabb.cpp +++ b/modules/gdnative/gdnative/aabb.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/gdnative/array.cpp b/modules/gdnative/gdnative/array.cpp index 863889acbc..7099b9d459 100644 --- a/modules/gdnative/gdnative/array.cpp +++ b/modules/gdnative/gdnative/array.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -215,9 +215,9 @@ godot_int GDAPI godot_array_count(const godot_array *p_self, const godot_variant return self->count(*val); } -godot_bool GDAPI godot_array_empty(const godot_array *p_self) { +godot_bool GDAPI godot_array_is_empty(const godot_array *p_self) { const Array *self = (const Array *)p_self; - return self->empty(); + return self->is_empty(); } void GDAPI godot_array_erase(godot_array *p_self, const godot_variant *p_value) { diff --git a/modules/gdnative/gdnative/basis.cpp b/modules/gdnative/gdnative/basis.cpp index e5891562a1..bfcd9bbf2c 100644 --- a/modules/gdnative/gdnative/basis.cpp +++ b/modules/gdnative/gdnative/basis.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/gdnative/callable.cpp b/modules/gdnative/gdnative/callable.cpp index f200e9f171..d4730a14b3 100644 --- a/modules/gdnative/gdnative/callable.cpp +++ b/modules/gdnative/gdnative/callable.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/gdnative/color.cpp b/modules/gdnative/gdnative/color.cpp index a93181e142..939dec3a47 100644 --- a/modules/gdnative/gdnative/color.cpp +++ b/modules/gdnative/gdnative/color.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/gdnative/dictionary.cpp b/modules/gdnative/gdnative/dictionary.cpp index b6900b28bb..f3c040428a 100644 --- a/modules/gdnative/gdnative/dictionary.cpp +++ b/modules/gdnative/gdnative/dictionary.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -71,9 +71,9 @@ godot_int GDAPI godot_dictionary_size(const godot_dictionary *p_self) { return self->size(); } -godot_bool GDAPI godot_dictionary_empty(const godot_dictionary *p_self) { +godot_bool GDAPI godot_dictionary_is_empty(const godot_dictionary *p_self) { const Dictionary *self = (const Dictionary *)p_self; - return self->empty(); + return self->is_empty(); } void GDAPI godot_dictionary_clear(godot_dictionary *p_self) { diff --git a/modules/gdnative/gdnative/gdnative.cpp b/modules/gdnative/gdnative/gdnative.cpp index 4142ea892b..1c11130d89 100644 --- a/modules/gdnative/gdnative/gdnative.cpp +++ b/modules/gdnative/gdnative/gdnative.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/gdnative/node_path.cpp b/modules/gdnative/gdnative/node_path.cpp index c031498612..7b215c0d0b 100644 --- a/modules/gdnative/gdnative/node_path.cpp +++ b/modules/gdnative/gdnative/node_path.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/gdnative/packed_arrays.cpp b/modules/gdnative/gdnative/packed_arrays.cpp index cc1e05b8a4..e714999234 100644 --- a/modules/gdnative/gdnative/packed_arrays.cpp +++ b/modules/gdnative/gdnative/packed_arrays.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -150,9 +150,9 @@ godot_int GDAPI godot_packed_byte_array_size(const godot_packed_byte_array *p_se return self->size(); } -godot_bool GDAPI godot_packed_byte_array_empty(const godot_packed_byte_array *p_self) { +godot_bool GDAPI godot_packed_byte_array_is_empty(const godot_packed_byte_array *p_self) { const Vector<uint8_t> *self = (const Vector<uint8_t> *)p_self; - return self->empty(); + return self->is_empty(); } void GDAPI godot_packed_byte_array_destroy(godot_packed_byte_array *p_self) { @@ -254,9 +254,9 @@ godot_int GDAPI godot_packed_int32_array_size(const godot_packed_int32_array *p_ return self->size(); } -godot_bool GDAPI godot_packed_int32_array_empty(const godot_packed_int32_array *p_self) { +godot_bool GDAPI godot_packed_int32_array_is_empty(const godot_packed_int32_array *p_self) { const Vector<int32_t> *self = (const Vector<int32_t> *)p_self; - return self->empty(); + return self->is_empty(); } void GDAPI godot_packed_int32_array_destroy(godot_packed_int32_array *p_self) { @@ -358,9 +358,9 @@ godot_int GDAPI godot_packed_int64_array_size(const godot_packed_int64_array *p_ return self->size(); } -godot_bool GDAPI godot_packed_int64_array_empty(const godot_packed_int64_array *p_self) { +godot_bool GDAPI godot_packed_int64_array_is_empty(const godot_packed_int64_array *p_self) { const Vector<int64_t> *self = (const Vector<int64_t> *)p_self; - return self->empty(); + return self->is_empty(); } void GDAPI godot_packed_int64_array_destroy(godot_packed_int64_array *p_self) { @@ -462,9 +462,9 @@ godot_int GDAPI godot_packed_float32_array_size(const godot_packed_float32_array return self->size(); } -godot_bool GDAPI godot_packed_float32_array_empty(const godot_packed_float32_array *p_self) { +godot_bool GDAPI godot_packed_float32_array_is_empty(const godot_packed_float32_array *p_self) { const Vector<float> *self = (const Vector<float> *)p_self; - return self->empty(); + return self->is_empty(); } void GDAPI godot_packed_float32_array_destroy(godot_packed_float32_array *p_self) { @@ -566,9 +566,9 @@ godot_int GDAPI godot_packed_float64_array_size(const godot_packed_float64_array return self->size(); } -godot_bool GDAPI godot_packed_float64_array_empty(const godot_packed_float64_array *p_self) { +godot_bool GDAPI godot_packed_float64_array_is_empty(const godot_packed_float64_array *p_self) { const Vector<double> *self = (const Vector<double> *)p_self; - return self->empty(); + return self->is_empty(); } void GDAPI godot_packed_float64_array_destroy(godot_packed_float64_array *p_self) { @@ -679,9 +679,9 @@ godot_int GDAPI godot_packed_string_array_size(const godot_packed_string_array * return self->size(); } -godot_bool GDAPI godot_packed_string_array_empty(const godot_packed_string_array *p_self) { +godot_bool GDAPI godot_packed_string_array_is_empty(const godot_packed_string_array *p_self) { const Vector<String> *self = (const Vector<String> *)p_self; - return self->empty(); + return self->is_empty(); } void GDAPI godot_packed_string_array_destroy(godot_packed_string_array *p_self) { @@ -791,9 +791,9 @@ godot_int GDAPI godot_packed_vector2_array_size(const godot_packed_vector2_array return self->size(); } -godot_bool GDAPI godot_packed_vector2_array_empty(const godot_packed_vector2_array *p_self) { +godot_bool GDAPI godot_packed_vector2_array_is_empty(const godot_packed_vector2_array *p_self) { const Vector<Vector2> *self = (const Vector<Vector2> *)p_self; - return self->empty(); + return self->is_empty(); } void GDAPI godot_packed_vector2_array_destroy(godot_packed_vector2_array *p_self) { @@ -903,9 +903,9 @@ godot_int GDAPI godot_packed_vector2i_array_size(const godot_packed_vector2i_arr return self->size(); } -godot_bool GDAPI godot_packed_vector2i_array_empty(const godot_packed_vector2i_array *p_self) { +godot_bool GDAPI godot_packed_vector2i_array_is_empty(const godot_packed_vector2i_array *p_self) { const Vector<Vector2i> *self = (const Vector<Vector2i> *)p_self; - return self->empty(); + return self->is_empty(); } void GDAPI godot_packed_vector2i_array_destroy(godot_packed_vector2i_array *p_self) { @@ -1015,9 +1015,9 @@ godot_int GDAPI godot_packed_vector3_array_size(const godot_packed_vector3_array return self->size(); } -godot_bool GDAPI godot_packed_vector3_array_empty(const godot_packed_vector3_array *p_self) { +godot_bool GDAPI godot_packed_vector3_array_is_empty(const godot_packed_vector3_array *p_self) { const Vector<Vector3> *self = (const Vector<Vector3> *)p_self; - return self->empty(); + return self->is_empty(); } void GDAPI godot_packed_vector3_array_destroy(godot_packed_vector3_array *p_self) { @@ -1127,9 +1127,9 @@ godot_int GDAPI godot_packed_color_array_size(const godot_packed_color_array *p_ return self->size(); } -godot_bool GDAPI godot_packed_color_array_empty(const godot_packed_color_array *p_self) { +godot_bool GDAPI godot_packed_color_array_is_empty(const godot_packed_color_array *p_self) { const Vector<Color> *self = (const Vector<Color> *)p_self; - return self->empty(); + return self->is_empty(); } void GDAPI godot_packed_color_array_destroy(godot_packed_color_array *p_self) { diff --git a/modules/gdnative/gdnative/plane.cpp b/modules/gdnative/gdnative/plane.cpp index 99fb5ff10a..32a90d08fa 100644 --- a/modules/gdnative/gdnative/plane.cpp +++ b/modules/gdnative/gdnative/plane.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/gdnative/quat.cpp b/modules/gdnative/gdnative/quat.cpp index a41886e780..29edad6636 100644 --- a/modules/gdnative/gdnative/quat.cpp +++ b/modules/gdnative/gdnative/quat.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/gdnative/rect2.cpp b/modules/gdnative/gdnative/rect2.cpp index bacefced5d..40e8e64ca1 100644 --- a/modules/gdnative/gdnative/rect2.cpp +++ b/modules/gdnative/gdnative/rect2.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -90,11 +90,11 @@ godot_bool GDAPI godot_rect2_has_no_area(const godot_rect2 *p_self) { return self->has_no_area(); } -godot_rect2 GDAPI godot_rect2_clip(const godot_rect2 *p_self, const godot_rect2 *p_b) { +godot_rect2 GDAPI godot_rect2_intersection(const godot_rect2 *p_self, const godot_rect2 *p_b) { godot_rect2 dest; const Rect2 *self = (const Rect2 *)p_self; const Rect2 *b = (const Rect2 *)p_b; - *((Rect2 *)&dest) = self->clip(*b); + *((Rect2 *)&dest) = self->intersection(*b); return dest; } @@ -127,10 +127,10 @@ godot_rect2 GDAPI godot_rect2_grow_individual(const godot_rect2 *p_self, const g return dest; } -godot_rect2 GDAPI godot_rect2_grow_margin(const godot_rect2 *p_self, const godot_int p_margin, const godot_real p_by) { +godot_rect2 GDAPI godot_rect2_grow_side(const godot_rect2 *p_self, const godot_int p_side, const godot_real p_by) { godot_rect2 dest; const Rect2 *self = (const Rect2 *)p_self; - *((Rect2 *)&dest) = self->grow_margin((Margin)p_margin, p_by); + *((Rect2 *)&dest) = self->grow_side((Side)p_side, p_by); return dest; } @@ -233,11 +233,11 @@ godot_bool GDAPI godot_rect2i_has_no_area(const godot_rect2i *p_self) { return self->has_no_area(); } -godot_rect2i GDAPI godot_rect2i_clip(const godot_rect2i *p_self, const godot_rect2i *p_b) { +godot_rect2i GDAPI godot_rect2i_intersection(const godot_rect2i *p_self, const godot_rect2i *p_b) { godot_rect2i dest; const Rect2i *self = (const Rect2i *)p_self; const Rect2i *b = (const Rect2i *)p_b; - *((Rect2i *)&dest) = self->clip(*b); + *((Rect2i *)&dest) = self->intersection(*b); return dest; } @@ -270,10 +270,10 @@ godot_rect2i GDAPI godot_rect2i_grow_individual(const godot_rect2i *p_self, cons return dest; } -godot_rect2i GDAPI godot_rect2i_grow_margin(const godot_rect2i *p_self, const godot_int p_margin, const godot_int p_by) { +godot_rect2i GDAPI godot_rect2i_grow_side(const godot_rect2i *p_self, const godot_int p_side, const godot_int p_by) { godot_rect2i dest; const Rect2i *self = (const Rect2i *)p_self; - *((Rect2i *)&dest) = self->grow_margin((Margin)p_margin, p_by); + *((Rect2i *)&dest) = self->grow_side((Side)p_side, p_by); return dest; } diff --git a/modules/gdnative/gdnative/rid.cpp b/modules/gdnative/gdnative/rid.cpp index 24af04558b..33685ef51f 100644 --- a/modules/gdnative/gdnative/rid.cpp +++ b/modules/gdnative/gdnative/rid.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/gdnative/string.cpp b/modules/gdnative/gdnative/string.cpp index 47c7f7b6e7..734bbe0d16 100644 --- a/modules/gdnative/gdnative/string.cpp +++ b/modules/gdnative/gdnative/string.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -1087,10 +1087,10 @@ godot_string GDAPI godot_string_sha256_text(const godot_string *p_self) { return result; } -godot_bool godot_string_empty(const godot_string *p_self) { +godot_bool godot_string_is_empty(const godot_string *p_self) { const String *self = (const String *)p_self; - return self->empty(); + return self->is_empty(); } // path functions @@ -1112,7 +1112,7 @@ godot_string GDAPI godot_string_get_file(const godot_string *p_self) { return result; } -godot_string GDAPI godot_string_humanize_size(size_t p_size) { +godot_string GDAPI godot_string_humanize_size(uint64_t p_size) { godot_string result; String return_value = String::humanize_size(p_size); memnew_placement(&result, String(return_value)); diff --git a/modules/gdnative/gdnative/string_name.cpp b/modules/gdnative/gdnative/string_name.cpp index a840d74e18..3d83f744d6 100644 --- a/modules/gdnative/gdnative/string_name.cpp +++ b/modules/gdnative/gdnative/string_name.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/gdnative/transform.cpp b/modules/gdnative/gdnative/transform.cpp index b17d6f8d4c..059e12b401 100644 --- a/modules/gdnative/gdnative/transform.cpp +++ b/modules/gdnative/gdnative/transform.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/gdnative/transform2d.cpp b/modules/gdnative/gdnative/transform2d.cpp index 3c1105e323..878599514d 100644 --- a/modules/gdnative/gdnative/transform2d.cpp +++ b/modules/gdnative/gdnative/transform2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/gdnative/variant.cpp b/modules/gdnative/gdnative/variant.cpp index 417abeaad3..7ee5fe59e2 100644 --- a/modules/gdnative/gdnative/variant.cpp +++ b/modules/gdnative/gdnative/variant.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/gdnative/vector2.cpp b/modules/gdnative/gdnative/vector2.cpp index 1ba846d315..6f42935228 100644 --- a/modules/gdnative/gdnative/vector2.cpp +++ b/modules/gdnative/gdnative/vector2.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -154,10 +154,10 @@ godot_vector2 GDAPI godot_vector2_rotated(const godot_vector2 *p_self, const god return dest; } -godot_vector2 GDAPI godot_vector2_tangent(const godot_vector2 *p_self) { +godot_vector2 GDAPI godot_vector2_orthogonal(const godot_vector2 *p_self) { godot_vector2 dest; const Vector2 *self = (const Vector2 *)p_self; - *((Vector2 *)&dest) = self->tangent(); + *((Vector2 *)&dest) = self->orthogonal(); return dest; } diff --git a/modules/gdnative/gdnative/vector3.cpp b/modules/gdnative/gdnative/vector3.cpp index 3284afdc31..75aeb59c87 100644 --- a/modules/gdnative/gdnative/vector3.cpp +++ b/modules/gdnative/gdnative/vector3.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json index 84b36b3745..a29a0808ca 100644 --- a/modules/gdnative/gdnative_api.json +++ b/modules/gdnative/gdnative_api.json @@ -380,7 +380,7 @@ ] }, { - "name": "godot_array_empty", + "name": "godot_array_is_empty", "return_type": "godot_bool", "arguments": [ ["const godot_array *", "p_self"] @@ -1388,7 +1388,7 @@ ] }, { - "name": "godot_dictionary_empty", + "name": "godot_dictionary_is_empty", "return_type": "godot_bool", "arguments": [ ["const godot_dictionary *", "p_self"] @@ -1647,7 +1647,7 @@ ] }, { - "name": "godot_packed_byte_array_empty", + "name": "godot_packed_byte_array_is_empty", "return_type": "godot_bool", "arguments": [ ["const godot_packed_byte_array *", "p_self"] @@ -1793,7 +1793,7 @@ ] }, { - "name": "godot_packed_int32_array_empty", + "name": "godot_packed_int32_array_is_empty", "return_type": "godot_bool", "arguments": [ ["const godot_packed_int32_array *", "p_self"] @@ -1939,7 +1939,7 @@ ] }, { - "name": "godot_packed_int64_array_empty", + "name": "godot_packed_int64_array_is_empty", "return_type": "godot_bool", "arguments": [ ["const godot_packed_int64_array *", "p_self"] @@ -2085,7 +2085,7 @@ ] }, { - "name": "godot_packed_float32_array_empty", + "name": "godot_packed_float32_array_is_empty", "return_type": "godot_bool", "arguments": [ ["const godot_packed_float32_array *", "p_self"] @@ -2231,7 +2231,7 @@ ] }, { - "name": "godot_packed_float64_array_empty", + "name": "godot_packed_float64_array_is_empty", "return_type": "godot_bool", "arguments": [ ["const godot_packed_float64_array *", "p_self"] @@ -2377,7 +2377,7 @@ ] }, { - "name": "godot_packed_string_array_empty", + "name": "godot_packed_string_array_is_empty", "return_type": "godot_bool", "arguments": [ ["const godot_packed_string_array *", "p_self"] @@ -2523,7 +2523,7 @@ ] }, { - "name": "godot_packed_vector2_array_empty", + "name": "godot_packed_vector2_array_is_empty", "return_type": "godot_bool", "arguments": [ ["const godot_packed_vector2_array *", "p_self"] @@ -2669,7 +2669,7 @@ ] }, { - "name": "godot_packed_vector2i_array_empty", + "name": "godot_packed_vector2i_array_is_empty", "return_type": "godot_bool", "arguments": [ ["const godot_packed_vector2i_array *", "p_self"] @@ -2815,7 +2815,7 @@ ] }, { - "name": "godot_packed_vector3_array_empty", + "name": "godot_packed_vector3_array_is_empty", "return_type": "godot_bool", "arguments": [ ["const godot_packed_vector3_array *", "p_self"] @@ -2961,7 +2961,7 @@ ] }, { - "name": "godot_packed_color_array_empty", + "name": "godot_packed_color_array_is_empty", "return_type": "godot_bool", "arguments": [ ["const godot_packed_color_array *", "p_self"] @@ -3534,11 +3534,11 @@ ] }, { - "name": "godot_rect2_grow_margin", + "name": "godot_rect2_grow_side", "return_type": "godot_rect2", "arguments": [ ["const godot_rect2 *", "p_self"], - ["const godot_int", "p_margin"], + ["const godot_int", "p_side"], ["const godot_real", "p_by"] ] }, @@ -3573,7 +3573,7 @@ ] }, { - "name": "godot_rect2_clip", + "name": "godot_rect2_intersection", "return_type": "godot_rect2", "arguments": [ ["const godot_rect2 *", "p_self"], @@ -3715,7 +3715,7 @@ ] }, { - "name": "godot_rect2i_clip", + "name": "godot_rect2i_intersection", "return_type": "godot_rect2i", "arguments": [ ["const godot_rect2i *", "p_self"], @@ -3758,11 +3758,11 @@ ] }, { - "name": "godot_rect2i_grow_margin", + "name": "godot_rect2i_grow_side", "return_type": "godot_rect2i", "arguments": [ ["const godot_rect2i *", "p_self"], - ["const godot_int", "p_margin"], + ["const godot_int", "p_side"], ["const godot_int", "p_by"] ] }, @@ -5005,7 +5005,7 @@ ] }, { - "name": "godot_string_empty", + "name": "godot_string_is_empty", "return_type": "godot_bool", "arguments": [ ["const godot_string *", "p_self"] @@ -5029,7 +5029,7 @@ "name": "godot_string_humanize_size", "return_type": "godot_string", "arguments": [ - ["size_t", "p_size"] + ["uint64_t", "p_size"] ] }, { @@ -6413,7 +6413,7 @@ ] }, { - "name": "godot_vector2_tangent", + "name": "godot_vector2_orthogonal", "return_type": "godot_vector2", "arguments": [ ["const godot_vector2 *", "p_self"] @@ -7946,7 +7946,7 @@ ] }, { - "name": "godot_packed_glyph_array_empty", + "name": "godot_packed_glyph_array_is_empty", "return_type": "godot_bool", "arguments": [ ["const godot_packed_glyph_array *", "p_self"] diff --git a/modules/gdnative/gdnative_library_editor_plugin.cpp b/modules/gdnative/gdnative_library_editor_plugin.cpp index f0f095ddf5..d3cca5b1be 100644 --- a/modules/gdnative/gdnative_library_editor_plugin.cpp +++ b/modules/gdnative/gdnative_library_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -65,7 +65,7 @@ void GDNativeLibraryEditor::_update_tree() { continue; } Map<String, NativePlatformConfig>::Element *E = platforms.find(filter_list->get_item_metadata(i)); - if (!text.empty()) { + if (!text.is_empty()) { text += ", "; } text += E->get().name; @@ -91,7 +91,7 @@ void GDNativeLibraryEditor::_update_tree() { bit->add_button(1, get_theme_icon("Folder", "EditorIcons"), BUTTON_SELECT_LIBRARY, false, TTR("Select the dynamic library for this entry")); String file = entry_configs[target].library; - if (!file.empty()) { + if (!file.is_empty()) { bit->add_button(1, get_theme_icon("Clear", "EditorIcons"), BUTTON_CLEAR_LIBRARY, false, TTR("Clear")); } bit->set_text(1, file); @@ -195,7 +195,7 @@ void GDNativeLibraryEditor::_on_item_activated() { void GDNativeLibraryEditor::_on_create_new_entry() { String platform = new_architecture_dialog->get_meta("platform"); String entry = new_architecture_input->get_text().strip_edges(); - if (!entry.empty()) { + if (!entry.is_empty()) { platforms[platform].entries.push_back(entry); _update_tree(); } @@ -248,7 +248,7 @@ void GDNativeLibraryEditor::_translate_to_config_file() { for (Map<String, NativePlatformConfig>::Element *E = platforms.front(); E; E = E->next()) { for (List<String>::Element *it = E->value().entries.front(); it; it = it->next()) { String target = E->key() + "." + it->get(); - if (entry_configs[target].library.empty() && entry_configs[target].dependencies.empty()) { + if (entry_configs[target].library.is_empty() && entry_configs[target].dependencies.is_empty()) { continue; } @@ -308,11 +308,11 @@ GDNativeLibraryEditor::GDNativeLibraryEditor() { platform_android.library_extension = "*.so"; platforms["Android"] = platform_android; - // TODO: Javascript platform is not supported yet - // NativePlatformConfig platform_html5; - // platform_html5.name = "HTML5"; - // platform_html5.library_extension = "*.wasm"; - // platforms["Javascript"] = platform_html5; + NativePlatformConfig platform_html5; + platform_html5.name = "HTML5"; + platform_html5.entries.push_back("wasm32"); + platform_html5.library_extension = "*.wasm"; + platforms["HTML5"] = platform_html5; NativePlatformConfig platform_ios; platform_ios.name = "iOS"; @@ -327,7 +327,7 @@ GDNativeLibraryEditor::GDNativeLibraryEditor() { VBoxContainer *container = memnew(VBoxContainer); add_child(container); - container->set_anchors_and_margins_preset(PRESET_WIDE); + container->set_anchors_and_offsets_preset(PRESET_WIDE); HBoxContainer *hbox = memnew(HBoxContainer); container->add_child(hbox); @@ -381,8 +381,8 @@ GDNativeLibraryEditor::GDNativeLibraryEditor() { new_architecture_input = memnew(LineEdit); new_architecture_dialog->add_child(new_architecture_input); // new_architecture_dialog->set_custom_minimum_size(Vector2(300, 80) * EDSCALE); - new_architecture_input->set_anchors_and_margins_preset(PRESET_HCENTER_WIDE, PRESET_MODE_MINSIZE, 5 * EDSCALE); - new_architecture_dialog->get_ok()->connect("pressed", callable_mp(this, &GDNativeLibraryEditor::_on_create_new_entry)); + new_architecture_input->set_anchors_and_offsets_preset(PRESET_HCENTER_WIDE, PRESET_MODE_MINSIZE, 5 * EDSCALE); + new_architecture_dialog->get_ok_button()->connect("pressed", callable_mp(this, &GDNativeLibraryEditor::_on_create_new_entry)); } void GDNativeLibraryEditorPlugin::edit(Object *p_node) { diff --git a/modules/gdnative/gdnative_library_editor_plugin.h b/modules/gdnative/gdnative_library_editor_plugin.h index 180ab7707c..184db3d817 100644 --- a/modules/gdnative/gdnative_library_editor_plugin.h +++ b/modules/gdnative/gdnative_library_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/gdnative_library_singleton_editor.cpp b/modules/gdnative/gdnative_library_singleton_editor.cpp index 409b6cbffe..f1b4a9a81b 100644 --- a/modules/gdnative/gdnative_library_singleton_editor.cpp +++ b/modules/gdnative/gdnative_library_singleton_editor.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/gdnative_library_singleton_editor.h b/modules/gdnative/gdnative_library_singleton_editor.h index 1a213d8094..5bb823d920 100644 --- a/modules/gdnative/gdnative_library_singleton_editor.h +++ b/modules/gdnative/gdnative_library_singleton_editor.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/android/godot_android.h b/modules/gdnative/include/android/godot_android.h index 45d4eaff37..867ef9e03a 100644 --- a/modules/gdnative/include/android/godot_android.h +++ b/modules/gdnative/include/android/godot_android.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/gdnative/aabb.h b/modules/gdnative/include/gdnative/aabb.h index c776297944..daf5ebfdd8 100644 --- a/modules/gdnative/include/gdnative/aabb.h +++ b/modules/gdnative/include/gdnative/aabb.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/gdnative/array.h b/modules/gdnative/include/gdnative/array.h index 7a59493b7d..9cc5bdfad5 100644 --- a/modules/gdnative/include/gdnative/array.h +++ b/modules/gdnative/include/gdnative/array.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -87,7 +87,7 @@ void GDAPI godot_array_clear(godot_array *p_self); godot_int GDAPI godot_array_count(const godot_array *p_self, const godot_variant *p_value); -godot_bool GDAPI godot_array_empty(const godot_array *p_self); +godot_bool GDAPI godot_array_is_empty(const godot_array *p_self); void GDAPI godot_array_erase(godot_array *p_self, const godot_variant *p_value); diff --git a/modules/gdnative/include/gdnative/basis.h b/modules/gdnative/include/gdnative/basis.h index c7425ebbfa..c6dab4c3c1 100644 --- a/modules/gdnative/include/gdnative/basis.h +++ b/modules/gdnative/include/gdnative/basis.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/gdnative/callable.h b/modules/gdnative/include/gdnative/callable.h index dbb5d02590..b3daaa7d0c 100644 --- a/modules/gdnative/include/gdnative/callable.h +++ b/modules/gdnative/include/gdnative/callable.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/gdnative/color.h b/modules/gdnative/include/gdnative/color.h index e64097ef57..c6ef921ad9 100644 --- a/modules/gdnative/include/gdnative/color.h +++ b/modules/gdnative/include/gdnative/color.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/gdnative/dictionary.h b/modules/gdnative/include/gdnative/dictionary.h index 873efaa9bf..3f664567d8 100644 --- a/modules/gdnative/include/gdnative/dictionary.h +++ b/modules/gdnative/include/gdnative/dictionary.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -67,7 +67,7 @@ godot_dictionary GDAPI godot_dictionary_duplicate(const godot_dictionary *p_self godot_int GDAPI godot_dictionary_size(const godot_dictionary *p_self); -godot_bool GDAPI godot_dictionary_empty(const godot_dictionary *p_self); +godot_bool GDAPI godot_dictionary_is_empty(const godot_dictionary *p_self); void GDAPI godot_dictionary_clear(godot_dictionary *p_self); diff --git a/modules/gdnative/include/gdnative/gdnative.h b/modules/gdnative/include/gdnative/gdnative.h index 6a0a375da8..cc8bf52fe4 100644 --- a/modules/gdnative/include/gdnative/gdnative.h +++ b/modules/gdnative/include/gdnative/gdnative.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/gdnative/node_path.h b/modules/gdnative/include/gdnative/node_path.h index 0cd0c3cb9c..052e4469a2 100644 --- a/modules/gdnative/include/gdnative/node_path.h +++ b/modules/gdnative/include/gdnative/node_path.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/gdnative/packed_arrays.h b/modules/gdnative/include/gdnative/packed_arrays.h index ce9579f307..f5b95eadd3 100644 --- a/modules/gdnative/include/gdnative/packed_arrays.h +++ b/modules/gdnative/include/gdnative/packed_arrays.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -195,7 +195,7 @@ uint8_t GDAPI godot_packed_byte_array_get(const godot_packed_byte_array *p_self, godot_int GDAPI godot_packed_byte_array_size(const godot_packed_byte_array *p_self); -godot_bool GDAPI godot_packed_byte_array_empty(const godot_packed_byte_array *p_self); +godot_bool GDAPI godot_packed_byte_array_is_empty(const godot_packed_byte_array *p_self); void GDAPI godot_packed_byte_array_destroy(godot_packed_byte_array *p_self); @@ -231,7 +231,7 @@ int32_t GDAPI godot_packed_int32_array_get(const godot_packed_int32_array *p_sel godot_int GDAPI godot_packed_int32_array_size(const godot_packed_int32_array *p_self); -godot_bool GDAPI godot_packed_int32_array_empty(const godot_packed_int32_array *p_self); +godot_bool GDAPI godot_packed_int32_array_is_empty(const godot_packed_int32_array *p_self); void GDAPI godot_packed_int32_array_destroy(godot_packed_int32_array *p_self); @@ -267,7 +267,7 @@ int64_t GDAPI godot_packed_int64_array_get(const godot_packed_int64_array *p_sel godot_int GDAPI godot_packed_int64_array_size(const godot_packed_int64_array *p_self); -godot_bool GDAPI godot_packed_int64_array_empty(const godot_packed_int64_array *p_self); +godot_bool GDAPI godot_packed_int64_array_is_empty(const godot_packed_int64_array *p_self); void GDAPI godot_packed_int64_array_destroy(godot_packed_int64_array *p_self); @@ -303,7 +303,7 @@ float GDAPI godot_packed_float32_array_get(const godot_packed_float32_array *p_s godot_int GDAPI godot_packed_float32_array_size(const godot_packed_float32_array *p_self); -godot_bool GDAPI godot_packed_float32_array_empty(const godot_packed_float32_array *p_self); +godot_bool GDAPI godot_packed_float32_array_is_empty(const godot_packed_float32_array *p_self); void GDAPI godot_packed_float32_array_destroy(godot_packed_float32_array *p_self); @@ -339,7 +339,7 @@ double GDAPI godot_packed_float64_array_get(const godot_packed_float64_array *p_ godot_int GDAPI godot_packed_float64_array_size(const godot_packed_float64_array *p_self); -godot_bool GDAPI godot_packed_float64_array_empty(const godot_packed_float64_array *p_self); +godot_bool GDAPI godot_packed_float64_array_is_empty(const godot_packed_float64_array *p_self); void GDAPI godot_packed_float64_array_destroy(godot_packed_float64_array *p_self); @@ -375,7 +375,7 @@ godot_string GDAPI godot_packed_string_array_get(const godot_packed_string_array godot_int GDAPI godot_packed_string_array_size(const godot_packed_string_array *p_self); -godot_bool GDAPI godot_packed_string_array_empty(const godot_packed_string_array *p_self); +godot_bool GDAPI godot_packed_string_array_is_empty(const godot_packed_string_array *p_self); void GDAPI godot_packed_string_array_destroy(godot_packed_string_array *p_self); @@ -411,7 +411,7 @@ godot_vector2 GDAPI godot_packed_vector2_array_get(const godot_packed_vector2_ar godot_int GDAPI godot_packed_vector2_array_size(const godot_packed_vector2_array *p_self); -godot_bool GDAPI godot_packed_vector2_array_empty(const godot_packed_vector2_array *p_self); +godot_bool GDAPI godot_packed_vector2_array_is_empty(const godot_packed_vector2_array *p_self); void GDAPI godot_packed_vector2_array_destroy(godot_packed_vector2_array *p_self); @@ -447,7 +447,7 @@ godot_vector2i GDAPI godot_packed_vector2i_array_get(const godot_packed_vector2i godot_int GDAPI godot_packed_vector2i_array_size(const godot_packed_vector2i_array *p_self); -godot_bool GDAPI godot_packed_vector2i_array_empty(const godot_packed_vector2i_array *p_self); +godot_bool GDAPI godot_packed_vector2i_array_is_empty(const godot_packed_vector2i_array *p_self); void GDAPI godot_packed_vector2i_array_destroy(godot_packed_vector2i_array *p_self); @@ -483,7 +483,7 @@ godot_vector3 GDAPI godot_packed_vector3_array_get(const godot_packed_vector3_ar godot_int GDAPI godot_packed_vector3_array_size(const godot_packed_vector3_array *p_self); -godot_bool GDAPI godot_packed_vector3_array_empty(const godot_packed_vector3_array *p_self); +godot_bool GDAPI godot_packed_vector3_array_is_empty(const godot_packed_vector3_array *p_self); void GDAPI godot_packed_vector3_array_destroy(godot_packed_vector3_array *p_self); @@ -519,7 +519,7 @@ godot_color GDAPI godot_packed_color_array_get(const godot_packed_color_array *p godot_int GDAPI godot_packed_color_array_size(const godot_packed_color_array *p_self); -godot_bool GDAPI godot_packed_color_array_empty(const godot_packed_color_array *p_self); +godot_bool GDAPI godot_packed_color_array_is_empty(const godot_packed_color_array *p_self); void GDAPI godot_packed_color_array_destroy(godot_packed_color_array *p_self); diff --git a/modules/gdnative/include/gdnative/plane.h b/modules/gdnative/include/gdnative/plane.h index 9843056489..a8625d4cd6 100644 --- a/modules/gdnative/include/gdnative/plane.h +++ b/modules/gdnative/include/gdnative/plane.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/gdnative/quat.h b/modules/gdnative/include/gdnative/quat.h index d315b2d754..68ca1765dd 100644 --- a/modules/gdnative/include/gdnative/quat.h +++ b/modules/gdnative/include/gdnative/quat.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/gdnative/rect2.h b/modules/gdnative/include/gdnative/rect2.h index f317afc9da..d3cb276e14 100644 --- a/modules/gdnative/include/gdnative/rect2.h +++ b/modules/gdnative/include/gdnative/rect2.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -80,7 +80,7 @@ godot_bool GDAPI godot_rect2_encloses(const godot_rect2 *p_self, const godot_rec godot_bool GDAPI godot_rect2_has_no_area(const godot_rect2 *p_self); -godot_rect2 GDAPI godot_rect2_clip(const godot_rect2 *p_self, const godot_rect2 *p_b); +godot_rect2 GDAPI godot_rect2_intersection(const godot_rect2 *p_self, const godot_rect2 *p_b); godot_rect2 GDAPI godot_rect2_merge(const godot_rect2 *p_self, const godot_rect2 *p_b); @@ -90,7 +90,7 @@ godot_rect2 GDAPI godot_rect2_grow(const godot_rect2 *p_self, const godot_real p godot_rect2 GDAPI godot_rect2_grow_individual(const godot_rect2 *p_self, const godot_real p_left, const godot_real p_top, const godot_real p_right, const godot_real p_bottom); -godot_rect2 GDAPI godot_rect2_grow_margin(const godot_rect2 *p_self, const godot_int p_margin, const godot_real p_by); +godot_rect2 GDAPI godot_rect2_grow_side(const godot_rect2 *p_self, const godot_int p_margin, const godot_real p_by); godot_rect2 GDAPI godot_rect2_abs(const godot_rect2 *p_self); @@ -123,7 +123,7 @@ godot_bool GDAPI godot_rect2i_encloses(const godot_rect2i *p_self, const godot_r godot_bool GDAPI godot_rect2i_has_no_area(const godot_rect2i *p_self); -godot_rect2i GDAPI godot_rect2i_clip(const godot_rect2i *p_self, const godot_rect2i *p_b); +godot_rect2i GDAPI godot_rect2i_intersection(const godot_rect2i *p_self, const godot_rect2i *p_b); godot_rect2i GDAPI godot_rect2i_merge(const godot_rect2i *p_self, const godot_rect2i *p_b); @@ -133,7 +133,7 @@ godot_rect2i GDAPI godot_rect2i_grow(const godot_rect2i *p_self, const godot_int godot_rect2i GDAPI godot_rect2i_grow_individual(const godot_rect2i *p_self, const godot_int p_left, const godot_int p_top, const godot_int p_right, const godot_int p_bottom); -godot_rect2i GDAPI godot_rect2i_grow_margin(const godot_rect2i *p_self, const godot_int p_margin, const godot_int p_by); +godot_rect2i GDAPI godot_rect2i_grow_side(const godot_rect2i *p_self, const godot_int p_margin, const godot_int p_by); godot_rect2i GDAPI godot_rect2i_abs(const godot_rect2i *p_self); diff --git a/modules/gdnative/include/gdnative/rid.h b/modules/gdnative/include/gdnative/rid.h index 73b601dc04..cbf066d47f 100644 --- a/modules/gdnative/include/gdnative/rid.h +++ b/modules/gdnative/include/gdnative/rid.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/gdnative/string.h b/modules/gdnative/include/gdnative/string.h index 6043351e84..e58be18b21 100644 --- a/modules/gdnative/include/gdnative/string.h +++ b/modules/gdnative/include/gdnative/string.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -256,12 +256,12 @@ godot_string GDAPI godot_string_sha1_text(const godot_string *p_self); godot_packed_byte_array GDAPI godot_string_sha256_buffer(const godot_string *p_self); godot_string GDAPI godot_string_sha256_text(const godot_string *p_self); -godot_bool godot_string_empty(const godot_string *p_self); +godot_bool godot_string_is_empty(const godot_string *p_self); // path functions godot_string GDAPI godot_string_get_base_dir(const godot_string *p_self); godot_string GDAPI godot_string_get_file(const godot_string *p_self); -godot_string GDAPI godot_string_humanize_size(size_t p_size); +godot_string GDAPI godot_string_humanize_size(uint64_t p_size); godot_bool GDAPI godot_string_is_abs_path(const godot_string *p_self); godot_bool GDAPI godot_string_is_rel_path(const godot_string *p_self); godot_bool GDAPI godot_string_is_resource_file(const godot_string *p_self); diff --git a/modules/gdnative/include/gdnative/string_name.h b/modules/gdnative/include/gdnative/string_name.h index f2555ab98f..b468f716e1 100644 --- a/modules/gdnative/include/gdnative/string_name.h +++ b/modules/gdnative/include/gdnative/string_name.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/gdnative/transform.h b/modules/gdnative/include/gdnative/transform.h index bc51438b17..948cb2ecfd 100644 --- a/modules/gdnative/include/gdnative/transform.h +++ b/modules/gdnative/include/gdnative/transform.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/gdnative/transform2d.h b/modules/gdnative/include/gdnative/transform2d.h index 6b414ca7b2..51c5306c7d 100644 --- a/modules/gdnative/include/gdnative/transform2d.h +++ b/modules/gdnative/include/gdnative/transform2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/gdnative/variant.h b/modules/gdnative/include/gdnative/variant.h index 0a611b76e9..a50947cb72 100644 --- a/modules/gdnative/include/gdnative/variant.h +++ b/modules/gdnative/include/gdnative/variant.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -52,7 +52,7 @@ typedef enum godot_variant_type { // atomic types GODOT_VARIANT_TYPE_BOOL, GODOT_VARIANT_TYPE_INT, - GODOT_VARIANT_TYPE_REAL, + GODOT_VARIANT_TYPE_FLOAT, GODOT_VARIANT_TYPE_STRING, // math types diff --git a/modules/gdnative/include/gdnative/vector2.h b/modules/gdnative/include/gdnative/vector2.h index 35b02c5a75..eb146a9232 100644 --- a/modules/gdnative/include/gdnative/vector2.h +++ b/modules/gdnative/include/gdnative/vector2.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -102,7 +102,7 @@ godot_vector2 GDAPI godot_vector2_move_toward(const godot_vector2 *p_self, const godot_vector2 GDAPI godot_vector2_rotated(const godot_vector2 *p_self, const godot_real p_phi); -godot_vector2 GDAPI godot_vector2_tangent(const godot_vector2 *p_self); +godot_vector2 GDAPI godot_vector2_orthogonal(const godot_vector2 *p_self); godot_vector2 GDAPI godot_vector2_floor(const godot_vector2 *p_self); diff --git a/modules/gdnative/include/gdnative/vector3.h b/modules/gdnative/include/gdnative/vector3.h index 5127b8789b..e0205c2fc7 100644 --- a/modules/gdnative/include/gdnative/vector3.h +++ b/modules/gdnative/include/gdnative/vector3.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/nativescript/godot_nativescript.h b/modules/gdnative/include/nativescript/godot_nativescript.h index cc12d58037..73b1738b03 100644 --- a/modules/gdnative/include/nativescript/godot_nativescript.h +++ b/modules/gdnative/include/nativescript/godot_nativescript.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/net/godot_net.h b/modules/gdnative/include/net/godot_net.h index 42804112f2..2fa576a5bf 100644 --- a/modules/gdnative/include/net/godot_net.h +++ b/modules/gdnative/include/net/godot_net.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/net/godot_webrtc.h b/modules/gdnative/include/net/godot_webrtc.h index 15e2df85cc..25aa72dae1 100644 --- a/modules/gdnative/include/net/godot_webrtc.h +++ b/modules/gdnative/include/net/godot_webrtc.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/pluginscript/godot_pluginscript.h b/modules/gdnative/include/pluginscript/godot_pluginscript.h index 406c3ba663..cbd65e3772 100644 --- a/modules/gdnative/include/pluginscript/godot_pluginscript.h +++ b/modules/gdnative/include/pluginscript/godot_pluginscript.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -72,6 +72,7 @@ typedef struct { godot_string_name name; godot_bool is_tool; godot_string_name base; + godot_string icon_path; // Member lines format: {<string>: <int>} godot_dictionary member_lines; @@ -127,6 +128,7 @@ typedef struct { const char **string_delimiters; // nullptr terminated array godot_bool has_named_classes; godot_bool supports_builtin_mode; + godot_bool can_inherit_from_file; godot_string (*get_template_source_code)(godot_pluginscript_language_data *p_data, const godot_string *p_class_name, const godot_string *p_base_class_name); godot_bool (*validate)(godot_pluginscript_language_data *p_data, const godot_string *p_script, int *r_line_error, int *r_col_error, godot_string *r_test_error, const godot_string *p_path, godot_packed_string_array *r_functions); diff --git a/modules/gdnative/include/text/godot_text.h b/modules/gdnative/include/text/godot_text.h index 2eac6adfb5..9de47edf87 100644 --- a/modules/gdnative/include/text/godot_text.h +++ b/modules/gdnative/include/text/godot_text.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -82,6 +82,9 @@ typedef struct { void (*font_set_antialiased)(void *, godot_rid *, bool); bool (*font_get_antialiased)(void *, godot_rid *); godot_dictionary (*font_get_feature_list)(void *, godot_rid *); + godot_dictionary (*font_get_variation_list)(void *, godot_rid *); + void (*font_set_variation)(void *, godot_rid *, const godot_string *, double); + double (*font_get_variation)(void *, godot_rid *, const godot_string *); void (*font_set_distance_field_hint)(void *, godot_rid *, bool); bool (*font_get_distance_field_hint)(void *, godot_rid *); void (*font_set_hinting)(void *, godot_rid *, godot_int); @@ -215,7 +218,7 @@ godot_glyph GDAPI godot_packed_glyph_array_get(const godot_packed_glyph_array *p godot_int GDAPI godot_packed_glyph_array_size(const godot_packed_glyph_array *p_self); -godot_bool GDAPI godot_packed_glyph_array_empty(const godot_packed_glyph_array *p_self); +godot_bool GDAPI godot_packed_glyph_array_is_empty(const godot_packed_glyph_array *p_self); void GDAPI godot_packed_glyph_array_destroy(godot_packed_glyph_array *p_self); diff --git a/modules/gdnative/include/videodecoder/godot_videodecoder.h b/modules/gdnative/include/videodecoder/godot_videodecoder.h index 16c92abd22..e5a2657997 100644 --- a/modules/gdnative/include/videodecoder/godot_videodecoder.h +++ b/modules/gdnative/include/videodecoder/godot_videodecoder.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/xr/godot_xr.h b/modules/gdnative/include/xr/godot_xr.h index 22f7f021c4..235242bc84 100644 --- a/modules/gdnative/include/xr/godot_xr.h +++ b/modules/gdnative/include/xr/godot_xr.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/nativescript/api_generator.cpp b/modules/gdnative/nativescript/api_generator.cpp index 6f2f9bfea9..6b46c9418a 100644 --- a/modules/gdnative/nativescript/api_generator.cpp +++ b/modules/gdnative/nativescript/api_generator.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -295,7 +295,7 @@ List<ClassAPI> generate_c_api_classes() { property_api.index = ClassDB::get_property_index(class_name, p->get().name); - if (!property_api.setter.empty() || !property_api.getter.empty()) { + if (!property_api.setter.is_empty() || !property_api.getter.is_empty()) { class_api.properties.push_back(property_api); } } diff --git a/modules/gdnative/nativescript/api_generator.h b/modules/gdnative/nativescript/api_generator.h index 8555af5215..a324ded4a9 100644 --- a/modules/gdnative/nativescript/api_generator.h +++ b/modules/gdnative/nativescript/api_generator.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/nativescript/godot_nativescript.cpp b/modules/gdnative/nativescript/godot_nativescript.cpp index 411acbe1ad..b2abf8b8ae 100644 --- a/modules/gdnative/nativescript/godot_nativescript.cpp +++ b/modules/gdnative/nativescript/godot_nativescript.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp index 0939cfd06a..e08961564d 100644 --- a/modules/gdnative/nativescript/nativescript.cpp +++ b/modules/gdnative/nativescript/nativescript.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -717,7 +717,7 @@ String NativeScript::get_property_documentation(const StringName &p_path) const } Variant NativeScript::_new(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - if (lib_path.empty() || class_name.empty() || library.is_null()) { + if (lib_path.is_empty() || class_name.is_empty() || library.is_null()) { r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL; return Variant(); } @@ -1800,7 +1800,7 @@ bool NativeScriptLanguage::handles_global_class_type(const String &p_type) const } String NativeScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const { - if (!p_path.empty()) { + if (!p_path.is_empty()) { Ref<NativeScript> script = ResourceLoader::load(p_path, "NativeScript"); if (script.is_valid()) { if (r_base_type) { diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h index 2aaa4be325..9d72bf39d1 100644 --- a/modules/gdnative/nativescript/nativescript.h +++ b/modules/gdnative/nativescript/nativescript.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,6 +31,7 @@ #ifndef NATIVE_SCRIPT_H #define NATIVE_SCRIPT_H +#include "core/doc_data.h" #include "core/io/resource.h" #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" @@ -152,6 +153,13 @@ public: virtual void set_source_code(const String &p_code) override; virtual Error reload(bool p_keep_state = false) override; +#ifdef TOOLS_ENABLED + virtual const Vector<DocData::ClassDoc> &get_documentation() const override { + static Vector<DocData::ClassDoc> docs; + return docs; + } +#endif // TOOLS_ENABLED + virtual bool has_method(const StringName &p_method) const override; virtual MethodInfo get_method_info(const StringName &p_method) const override; diff --git a/modules/gdnative/nativescript/register_types.cpp b/modules/gdnative/nativescript/register_types.cpp index ac8c7ab2fd..0353ab2092 100644 --- a/modules/gdnative/nativescript/register_types.cpp +++ b/modules/gdnative/nativescript/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/nativescript/register_types.h b/modules/gdnative/nativescript/register_types.h index 088bf38dd5..d12ac9eda3 100644 --- a/modules/gdnative/nativescript/register_types.h +++ b/modules/gdnative/nativescript/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/net/multiplayer_peer_gdnative.cpp b/modules/gdnative/net/multiplayer_peer_gdnative.cpp index 997eec6425..8b5fc8db5c 100644 --- a/modules/gdnative/net/multiplayer_peer_gdnative.cpp +++ b/modules/gdnative/net/multiplayer_peer_gdnative.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/net/multiplayer_peer_gdnative.h b/modules/gdnative/net/multiplayer_peer_gdnative.h index 64d764029f..593b2534dd 100644 --- a/modules/gdnative/net/multiplayer_peer_gdnative.h +++ b/modules/gdnative/net/multiplayer_peer_gdnative.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/net/packet_peer_gdnative.cpp b/modules/gdnative/net/packet_peer_gdnative.cpp index 6bb21cb48d..3bcdfed8ff 100644 --- a/modules/gdnative/net/packet_peer_gdnative.cpp +++ b/modules/gdnative/net/packet_peer_gdnative.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/net/packet_peer_gdnative.h b/modules/gdnative/net/packet_peer_gdnative.h index 00de8f7f4c..29013f9367 100644 --- a/modules/gdnative/net/packet_peer_gdnative.h +++ b/modules/gdnative/net/packet_peer_gdnative.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/net/register_types.cpp b/modules/gdnative/net/register_types.cpp index 4e48a43210..645c43b7e3 100644 --- a/modules/gdnative/net/register_types.cpp +++ b/modules/gdnative/net/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/net/register_types.h b/modules/gdnative/net/register_types.h index 70b266f9b9..c99c6f6fbf 100644 --- a/modules/gdnative/net/register_types.h +++ b/modules/gdnative/net/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/net/stream_peer_gdnative.cpp b/modules/gdnative/net/stream_peer_gdnative.cpp index 9dcb184115..72ab72323d 100644 --- a/modules/gdnative/net/stream_peer_gdnative.cpp +++ b/modules/gdnative/net/stream_peer_gdnative.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/net/stream_peer_gdnative.h b/modules/gdnative/net/stream_peer_gdnative.h index 302fb48012..dd5abceb83 100644 --- a/modules/gdnative/net/stream_peer_gdnative.h +++ b/modules/gdnative/net/stream_peer_gdnative.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/net/webrtc_gdnative.cpp b/modules/gdnative/net/webrtc_gdnative.cpp index d8c3ddc5f8..76ccbad009 100644 --- a/modules/gdnative/net/webrtc_gdnative.cpp +++ b/modules/gdnative/net/webrtc_gdnative.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/pluginscript/pluginscript_instance.cpp b/modules/gdnative/pluginscript/pluginscript_instance.cpp index 0942fb40a8..432aa80325 100644 --- a/modules/gdnative/pluginscript/pluginscript_instance.cpp +++ b/modules/gdnative/pluginscript/pluginscript_instance.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/pluginscript/pluginscript_instance.h b/modules/gdnative/pluginscript/pluginscript_instance.h index 76ff9f7097..865080fddf 100644 --- a/modules/gdnative/pluginscript/pluginscript_instance.h +++ b/modules/gdnative/pluginscript/pluginscript_instance.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/pluginscript/pluginscript_language.cpp b/modules/gdnative/pluginscript/pluginscript_language.cpp index fc9c4ebd77..3ed1dcaca9 100644 --- a/modules/gdnative/pluginscript/pluginscript_language.cpp +++ b/modules/gdnative/pluginscript/pluginscript_language.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -142,6 +142,10 @@ bool PluginScriptLanguage::supports_builtin_mode() const { return _desc.supports_builtin_mode; } +bool PluginScriptLanguage::can_inherit_from_file() const { + return _desc.can_inherit_from_file; +} + int PluginScriptLanguage::find_function(const String &p_function, const String &p_code) const { if (_desc.find_function) { return _desc.find_function(_data, (godot_string *)&p_function, (godot_string *)&p_code); @@ -398,6 +402,32 @@ void PluginScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool #endif } +bool PluginScriptLanguage::handles_global_class_type(const String &p_type) const { + return p_type == "PluginScript"; +} + +String PluginScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const { + if (!p_path.is_empty()) { + Ref<PluginScript> script = ResourceLoader::load(p_path, "PluginScript"); + if (script.is_valid()) { + if (r_base_type) { + *r_base_type = script->get_instance_base_type(); + } + if (r_icon_path) { + *r_icon_path = script->get_script_class_icon_path(); + } + return script->get_script_class_name(); + } + if (r_base_type) { + *r_base_type = String(); + } + if (r_icon_path) { + *r_icon_path = String(); + } + } + return String(); +} + void PluginScriptLanguage::lock() { _lock.lock(); } diff --git a/modules/gdnative/pluginscript/pluginscript_language.h b/modules/gdnative/pluginscript/pluginscript_language.h index 53e1e3ae9b..226b039265 100644 --- a/modules/gdnative/pluginscript/pluginscript_language.h +++ b/modules/gdnative/pluginscript/pluginscript_language.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -78,7 +78,7 @@ public: virtual Script *create_script() const; virtual bool has_named_classes() const; virtual bool supports_builtin_mode() const; - virtual bool can_inherit_from_file() { return true; } + virtual bool can_inherit_from_file() const; virtual int find_function(const String &p_function, const String &p_code) const; virtual String make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const; virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_force, String &r_call_hint); @@ -122,6 +122,11 @@ public: virtual void frame(); + /* GLOBAL CLASSES */ + + virtual bool handles_global_class_type(const String &p_type) const; + virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr) const; + void lock(); void unlock(); diff --git a/modules/gdnative/pluginscript/pluginscript_loader.cpp b/modules/gdnative/pluginscript/pluginscript_loader.cpp index 4feee4f4a5..cd1879a13e 100644 --- a/modules/gdnative/pluginscript/pluginscript_loader.cpp +++ b/modules/gdnative/pluginscript/pluginscript_loader.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/pluginscript/pluginscript_loader.h b/modules/gdnative/pluginscript/pluginscript_loader.h index 7d80f4c733..7b1a7f5423 100644 --- a/modules/gdnative/pluginscript/pluginscript_loader.h +++ b/modules/gdnative/pluginscript/pluginscript_loader.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/pluginscript/pluginscript_script.cpp b/modules/gdnative/pluginscript/pluginscript_script.cpp index 87c6288806..31e6a81975 100644 --- a/modules/gdnative/pluginscript/pluginscript_script.cpp +++ b/modules/gdnative/pluginscript/pluginscript_script.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -302,6 +302,7 @@ Error PluginScript::reload(bool p_keep_state) { _data = manifest.data; _name = *(StringName *)&manifest.name; _tool = manifest.is_tool; + _icon_path = *(String *)&manifest.icon_path; Dictionary *members = (Dictionary *)&manifest.member_lines; for (const Variant *key = members->next(); key != nullptr; key = members->next(key)) { diff --git a/modules/gdnative/pluginscript/pluginscript_script.h b/modules/gdnative/pluginscript/pluginscript_script.h index 150de05e74..1c86f2056d 100644 --- a/modules/gdnative/pluginscript/pluginscript_script.h +++ b/modules/gdnative/pluginscript/pluginscript_script.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,6 +32,8 @@ #define PLUGINSCRIPT_SCRIPT_H // Godot imports + +#include "core/doc_data.h" #include "core/object/script_language.h" // PluginScript imports #include "pluginscript_language.h" @@ -67,6 +69,7 @@ private: String _source; String _path; StringName _name; + String _icon_path; protected: static void _bind_methods(); @@ -82,6 +85,14 @@ protected: virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder) override; #endif public: + String get_script_class_name() const { + return _name; + } + + String get_script_class_icon_path() const { + return _icon_path; + } + virtual bool can_instance() const override; virtual Ref<Script> get_base_script() const override; //for script inheritance @@ -97,6 +108,13 @@ public: // TODO: load_source_code only allow utf-8 file, should handle bytecode as well ? virtual Error load_source_code(const String &p_path); +#ifdef TOOLS_ENABLED + virtual const Vector<DocData::ClassDoc> &get_documentation() const override { + static Vector<DocData::ClassDoc> docs; + return docs; + } +#endif // TOOLS_ENABLED + virtual bool has_method(const StringName &p_method) const override; virtual MethodInfo get_method_info(const StringName &p_method) const override; diff --git a/modules/gdnative/pluginscript/register_types.cpp b/modules/gdnative/pluginscript/register_types.cpp index b354c23a9e..b94538b2f7 100644 --- a/modules/gdnative/pluginscript/register_types.cpp +++ b/modules/gdnative/pluginscript/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/pluginscript/register_types.h b/modules/gdnative/pluginscript/register_types.h index c6a64b4f40..2118f668e9 100644 --- a/modules/gdnative/pluginscript/register_types.h +++ b/modules/gdnative/pluginscript/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/register_types.cpp b/modules/gdnative/register_types.cpp index b88bf58256..31f4fecb19 100644 --- a/modules/gdnative/register_types.cpp +++ b/modules/gdnative/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/register_types.h b/modules/gdnative/register_types.h index b5c182f8b7..662c638442 100644 --- a/modules/gdnative/register_types.h +++ b/modules/gdnative/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/tests/test_string.h b/modules/gdnative/tests/test_string.h index 2b1aa5bf28..3e2ba7451b 100644 --- a/modules/gdnative/tests/test_string.h +++ b/modules/gdnative/tests/test_string.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -259,11 +259,11 @@ TEST_CASE("[GDNative String] Testing for empty string") { godot_string s; godot_string_new_with_latin1_chars(&s, "Mellon"); - CHECK(!godot_string_empty(&s)); + CHECK(!godot_string_is_empty(&s)); godot_string_destroy(&s); godot_string_new_with_latin1_chars(&s, ""); - CHECK(godot_string_empty(&s)); + CHECK(godot_string_is_empty(&s)); godot_string_destroy(&s); } diff --git a/modules/gdnative/text/register_types.cpp b/modules/gdnative/text/register_types.cpp index e1d4547aa0..67385d2fbf 100644 --- a/modules/gdnative/text/register_types.cpp +++ b/modules/gdnative/text/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/text/register_types.h b/modules/gdnative/text/register_types.h index 027653e58e..cd4f2a3089 100644 --- a/modules/gdnative/text/register_types.h +++ b/modules/gdnative/text/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/text/text_server_gdnative.cpp b/modules/gdnative/text/text_server_gdnative.cpp index 68624260a6..f670d4af6f 100644 --- a/modules/gdnative/text/text_server_gdnative.cpp +++ b/modules/gdnative/text/text_server_gdnative.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -148,6 +148,24 @@ bool TextServerGDNative::font_get_antialiased(RID p_font) const { return interface->font_get_antialiased(data, (godot_rid *)&p_font); } +Dictionary TextServerGDNative::font_get_variation_list(RID p_font) const { + ERR_FAIL_COND_V(interface == nullptr, Dictionary()); + godot_dictionary result = interface->font_get_variation_list(data, (godot_rid *)&p_font); + Dictionary info = *(Dictionary *)&result; + godot_dictionary_destroy(&result); + + return info; +} + +void TextServerGDNative::font_set_variation(RID p_font, const String &p_name, double p_value) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_variation(data, (godot_rid *)&p_font, (godot_string *)&p_name, p_value); +} + +double TextServerGDNative::font_get_variation(RID p_font, const String &p_name) const { + return interface->font_get_variation(data, (godot_rid *)&p_font, (godot_string *)&p_name); +} + void TextServerGDNative::font_set_hinting(RID p_font, TextServer::Hinting p_hinting) { ERR_FAIL_COND(interface == nullptr); interface->font_set_hinting(data, (godot_rid *)&p_font, (godot_int)p_hinting); @@ -821,9 +839,9 @@ godot_int GDAPI godot_packed_glyph_array_size(const godot_packed_glyph_array *p_ return self->size(); } -godot_bool GDAPI godot_packed_glyph_array_empty(const godot_packed_glyph_array *p_self) { +godot_bool GDAPI godot_packed_glyph_array_is_empty(const godot_packed_glyph_array *p_self) { const Vector<TextServer::Glyph> *self = (const Vector<TextServer::Glyph> *)p_self; - return self->empty(); + return self->is_empty(); } void GDAPI godot_packed_glyph_array_destroy(godot_packed_glyph_array *p_self) { diff --git a/modules/gdnative/text/text_server_gdnative.h b/modules/gdnative/text/text_server_gdnative.h index 0196120c00..9cbb94217e 100644 --- a/modules/gdnative/text/text_server_gdnative.h +++ b/modules/gdnative/text/text_server_gdnative.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -76,6 +76,10 @@ public: virtual bool font_get_antialiased(RID p_font) const override; virtual Dictionary font_get_feature_list(RID p_font) const override; + virtual Dictionary font_get_variation_list(RID p_font) const override; + + virtual void font_set_variation(RID p_font, const String &p_name, double p_value) override; + virtual double font_get_variation(RID p_font, const String &p_name) const override; virtual void font_set_hinting(RID p_font, Hinting p_hinting) override; virtual Hinting font_get_hinting(RID p_font) const override; diff --git a/modules/gdnative/videodecoder/register_types.cpp b/modules/gdnative/videodecoder/register_types.cpp index 8ee1c8d183..394831daeb 100644 --- a/modules/gdnative/videodecoder/register_types.cpp +++ b/modules/gdnative/videodecoder/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/videodecoder/register_types.h b/modules/gdnative/videodecoder/register_types.h index b1a83d4071..809225c925 100644 --- a/modules/gdnative/videodecoder/register_types.h +++ b/modules/gdnative/videodecoder/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/videodecoder/video_stream_gdnative.cpp b/modules/gdnative/videodecoder/video_stream_gdnative.cpp index 61e882f2fe..18d26a9528 100644 --- a/modules/gdnative/videodecoder/video_stream_gdnative.cpp +++ b/modules/gdnative/videodecoder/video_stream_gdnative.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/videodecoder/video_stream_gdnative.h b/modules/gdnative/videodecoder/video_stream_gdnative.h index 408d4a2454..e64cda6602 100644 --- a/modules/gdnative/videodecoder/video_stream_gdnative.h +++ b/modules/gdnative/videodecoder/video_stream_gdnative.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/xr/register_types.cpp b/modules/gdnative/xr/register_types.cpp index da3a7dc4b8..b60a04f470 100644 --- a/modules/gdnative/xr/register_types.cpp +++ b/modules/gdnative/xr/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/xr/register_types.h b/modules/gdnative/xr/register_types.h index 2501d28651..4e7469abe9 100644 --- a/modules/gdnative/xr/register_types.h +++ b/modules/gdnative/xr/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/xr/xr_interface_gdnative.cpp b/modules/gdnative/xr/xr_interface_gdnative.cpp index d03fc33935..d4fd2876b5 100644 --- a/modules/gdnative/xr/xr_interface_gdnative.cpp +++ b/modules/gdnative/xr/xr_interface_gdnative.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -302,12 +302,12 @@ godot_int GDAPI godot_xr_add_controller(char *p_device_name, godot_int p_hand, g ERR_FAIL_NULL_V(input, 0); XRPositionalTracker *new_tracker = memnew(XRPositionalTracker); - new_tracker->set_name(p_device_name); - new_tracker->set_type(XRServer::TRACKER_CONTROLLER); + new_tracker->set_tracker_name(p_device_name); + new_tracker->set_tracker_type(XRServer::TRACKER_CONTROLLER); if (p_hand == 1) { - new_tracker->set_hand(XRPositionalTracker::TRACKER_LEFT_HAND); + new_tracker->set_tracker_hand(XRPositionalTracker::TRACKER_HAND_LEFT); } else if (p_hand == 2) { - new_tracker->set_hand(XRPositionalTracker::TRACKER_RIGHT_HAND); + new_tracker->set_tracker_hand(XRPositionalTracker::TRACKER_HAND_RIGHT); } // also register as joystick... diff --git a/modules/gdnative/xr/xr_interface_gdnative.h b/modules/gdnative/xr/xr_interface_gdnative.h index de96487397..84bd8fc731 100644 --- a/modules/gdnative/xr/xr_interface_gdnative.h +++ b/modules/gdnative/xr/xr_interface_gdnative.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnavigation/SCsub b/modules/gdnavigation/SCsub index 877d601c6a..22b5509b32 100644 --- a/modules/gdnavigation/SCsub +++ b/modules/gdnavigation/SCsub @@ -5,6 +5,10 @@ Import("env_modules") env_navigation = env_modules.Clone() +# Thirdparty source files + +thirdparty_obj = [] + # Recast Thirdparty source files if env["builtin_recast"]: thirdparty_dir = "#thirdparty/recastnavigation/Recast/" @@ -23,28 +27,37 @@ if env["builtin_recast"]: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_navigation.Prepend(CPPPATH=[thirdparty_dir + "/Include"]) + env_navigation.Prepend(CPPPATH=[thirdparty_dir + "Include"]) env_thirdparty = env_navigation.Clone() env_thirdparty.disable_warnings() - env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) - + env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) # RVO Thirdparty source files if env["builtin_rvo2"]: - thirdparty_dir = "#thirdparty/rvo2" + thirdparty_dir = "#thirdparty/rvo2/" thirdparty_sources = [ - "/src/Agent.cpp", - "/src/KdTree.cpp", + "Agent.cpp", + "KdTree.cpp", ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_navigation.Prepend(CPPPATH=[thirdparty_dir + "/src"]) + env_navigation.Prepend(CPPPATH=[thirdparty_dir]) env_thirdparty = env_navigation.Clone() env_thirdparty.disable_warnings() - env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) + + +env.modules_sources += thirdparty_obj # Godot source files -env_navigation.add_source_files(env.modules_sources, "*.cpp") + +module_obj = [] + +env_navigation.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj + +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/gdnavigation/gd_navigation_server.cpp b/modules/gdnavigation/gd_navigation_server.cpp index c80cdcfeab..4f61ad5040 100644 --- a/modules/gdnavigation/gd_navigation_server.cpp +++ b/modules/gdnavigation/gd_navigation_server.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnavigation/gd_navigation_server.h b/modules/gdnavigation/gd_navigation_server.h index c00d60ced7..92f4ccfdd5 100644 --- a/modules/gdnavigation/gd_navigation_server.h +++ b/modules/gdnavigation/gd_navigation_server.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnavigation/nav_map.cpp b/modules/gdnavigation/nav_map.cpp index 7919e6a01f..2646a4cc0c 100644 --- a/modules/gdnavigation/nav_map.cpp +++ b/modules/gdnavigation/nav_map.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -707,7 +707,7 @@ void NavMap::sync() { } if (regenerate_links) { - map_update_id = map_update_id + 1 % 9999999; + map_update_id = (map_update_id + 1) % 9999999; } if (agents_dirty) { diff --git a/modules/gdnavigation/nav_map.h b/modules/gdnavigation/nav_map.h index 892755f3f9..bffc1fbc1a 100644 --- a/modules/gdnavigation/nav_map.h +++ b/modules/gdnavigation/nav_map.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnavigation/nav_region.cpp b/modules/gdnavigation/nav_region.cpp index 51fba67cc3..383b0f15a6 100644 --- a/modules/gdnavigation/nav_region.cpp +++ b/modules/gdnavigation/nav_region.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnavigation/nav_region.h b/modules/gdnavigation/nav_region.h index 731855bfb5..954780033b 100644 --- a/modules/gdnavigation/nav_region.h +++ b/modules/gdnavigation/nav_region.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnavigation/nav_rid.h b/modules/gdnavigation/nav_rid.h index b727fceb04..a0a60a3643 100644 --- a/modules/gdnavigation/nav_rid.h +++ b/modules/gdnavigation/nav_rid.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnavigation/nav_utils.h b/modules/gdnavigation/nav_utils.h index 40e54df553..d1d1687a1f 100644 --- a/modules/gdnavigation/nav_utils.h +++ b/modules/gdnavigation/nav_utils.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnavigation/navigation_mesh_editor_plugin.cpp b/modules/gdnavigation/navigation_mesh_editor_plugin.cpp index 648f4f7cdd..aa9248d2a1 100644 --- a/modules/gdnavigation/navigation_mesh_editor_plugin.cpp +++ b/modules/gdnavigation/navigation_mesh_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -142,7 +142,7 @@ void NavigationMeshEditorPlugin::make_visible(bool p_visible) { NavigationMeshEditorPlugin::NavigationMeshEditorPlugin(EditorNode *p_node) { editor = p_node; navigation_mesh_editor = memnew(NavigationMeshEditor); - editor->get_viewport()->add_child(navigation_mesh_editor); + editor->get_main_control()->add_child(navigation_mesh_editor); add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, navigation_mesh_editor->bake_hbox); navigation_mesh_editor->hide(); navigation_mesh_editor->bake_hbox->hide(); diff --git a/modules/gdnavigation/navigation_mesh_editor_plugin.h b/modules/gdnavigation/navigation_mesh_editor_plugin.h index f09182fff4..c39269865b 100644 --- a/modules/gdnavigation/navigation_mesh_editor_plugin.h +++ b/modules/gdnavigation/navigation_mesh_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnavigation/navigation_mesh_generator.cpp b/modules/gdnavigation/navigation_mesh_generator.cpp index 5329600e39..a7d4e79148 100644 --- a/modules/gdnavigation/navigation_mesh_generator.cpp +++ b/modules/gdnavigation/navigation_mesh_generator.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -151,7 +151,7 @@ void NavigationMeshGenerator::_parse_geometry(Transform p_accumulated_transform, if (Object::cast_to<CSGShape3D>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) { CSGShape3D *csg_shape = Object::cast_to<CSGShape3D>(p_node); Array meshes = csg_shape->get_meshes(); - if (!meshes.empty()) { + if (!meshes.is_empty()) { Ref<Mesh> mesh = meshes[1]; if (mesh.is_valid()) { _add_mesh(mesh, p_accumulated_transform * csg_shape->get_transform(), p_verticies, p_indices); @@ -176,10 +176,10 @@ void NavigationMeshGenerator::_parse_geometry(Transform p_accumulated_transform, BoxShape3D *box = Object::cast_to<BoxShape3D>(*s); if (box) { - Ref<CubeMesh> cube_mesh; - cube_mesh.instance(); - cube_mesh->set_size(box->get_extents() * 2.0); - mesh = cube_mesh; + Ref<BoxMesh> box_mesh; + box_mesh.instance(); + box_mesh->set_size(box->get_size()); + mesh = box_mesh; } CapsuleShape3D *capsule = Object::cast_to<CapsuleShape3D>(*s); diff --git a/modules/gdnavigation/navigation_mesh_generator.h b/modules/gdnavigation/navigation_mesh_generator.h index c5f7b2ab81..88ccdb1c41 100644 --- a/modules/gdnavigation/navigation_mesh_generator.h +++ b/modules/gdnavigation/navigation_mesh_generator.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnavigation/register_types.cpp b/modules/gdnavigation/register_types.cpp index 1ae19ebe47..8443d3d242 100644 --- a/modules/gdnavigation/register_types.cpp +++ b/modules/gdnavigation/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnavigation/register_types.h b/modules/gdnavigation/register_types.h index cdbff1b937..c2bb08c649 100644 --- a/modules/gdnavigation/register_types.h +++ b/modules/gdnavigation/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnavigation/rvo_agent.cpp b/modules/gdnavigation/rvo_agent.cpp index 1e1bdbd07d..21e43d08c1 100644 --- a/modules/gdnavigation/rvo_agent.cpp +++ b/modules/gdnavigation/rvo_agent.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnavigation/rvo_agent.h b/modules/gdnavigation/rvo_agent.h index de36508edb..d9e3345498 100644 --- a/modules/gdnavigation/rvo_agent.h +++ b/modules/gdnavigation/rvo_agent.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index d90b3e52d0..9e974b6fdc 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -32,60 +32,6 @@ [/codeblock] </description> </method> - <method name="ColorN"> - <return type="Color"> - </return> - <argument index="0" name="name" type="String"> - </argument> - <argument index="1" name="alpha" type="float" default="1.0"> - </argument> - <description> - Returns a color according to the standardized [code]name[/code] with [code]alpha[/code] ranging from 0 to 1. - [codeblock] - red = ColorN("red", 1) - [/codeblock] - Supported color names are the same as the constants defined in [Color]. - </description> - </method> - <method name="abs"> - <return type="float"> - </return> - <argument index="0" name="s" type="float"> - </argument> - <description> - Returns the absolute value of parameter [code]s[/code] (i.e. positive value). - [codeblock] - # a is 1 - a = abs(-1) - [/codeblock] - </description> - </method> - <method name="acos"> - <return type="float"> - </return> - <argument index="0" name="s" type="float"> - </argument> - <description> - Returns the arc cosine of [code]s[/code] in radians. Use to get the angle of cosine [code]s[/code]. - [codeblock] - # c is 0.523599 or 30 degrees if converted with rad2deg(s) - c = acos(0.866025) - [/codeblock] - </description> - </method> - <method name="asin"> - <return type="float"> - </return> - <argument index="0" name="s" type="float"> - </argument> - <description> - Returns the arc sine of [code]s[/code] in radians. Use to get the angle of sine [code]s[/code]. - [codeblock] - # s is 0.523599 or 30 degrees if converted with rad2deg(s) - s = asin(0.5) - [/codeblock] - </description> - </method> <method name="assert"> <return type="void"> </return> @@ -94,7 +40,7 @@ <argument index="1" name="message" type="String" default=""""> </argument> <description> - Asserts that the [code]condition[/code] is [code]true[/code]. If the [code]condition[/code] is [code]false[/code], an error is generated. When running from the editor, the running project will also be paused until you resume it. This can be used as a stronger form of [method push_error] for reporting errors to project developers or add-on users. + Asserts that the [code]condition[/code] is [code]true[/code]. If the [code]condition[/code] is [code]false[/code], an error is generated. When running from the editor, the running project will also be paused until you resume it. This can be used as a stronger form of [method @GlobalScope.push_error] for reporting errors to project developers or add-on users. [b]Note:[/b] For performance reasons, the code inside [method assert] is only executed in debug builds or when running the project from the editor. Don't include code that has side effects in an [method assert] call. Otherwise, the project will behave differently when exported in release mode. The optional [code]message[/code] argument, if given, is shown in addition to the generic "Assertion failed" message. You can use this to provide additional details about why the assertion failed. [codeblock] @@ -107,75 +53,10 @@ [/codeblock] </description> </method> - <method name="atan"> - <return type="float"> - </return> - <argument index="0" name="s" type="float"> - </argument> - <description> - Returns the arc tangent of [code]s[/code] in radians. Use it to get the angle from an angle's tangent in trigonometry: [code]atan(tan(angle)) == angle[/code]. - The method cannot know in which quadrant the angle should fall. See [method atan2] if you have both [code]y[/code] and [code]x[/code]. - [codeblock] - a = atan(0.5) # a is 0.463648 - [/codeblock] - </description> - </method> - <method name="atan2"> - <return type="float"> - </return> - <argument index="0" name="y" type="float"> - </argument> - <argument index="1" name="x" type="float"> - </argument> - <description> - Returns the arc tangent of [code]y/x[/code] in radians. Use to get the angle of tangent [code]y/x[/code]. To compute the value, the method takes into account the sign of both arguments in order to determine the quadrant. - Important note: The Y coordinate comes first, by convention. - [codeblock] - a = atan2(0, -1) # a is 3.141593 - [/codeblock] - </description> - </method> - <method name="bytes2var"> - <return type="Variant"> - </return> - <argument index="0" name="bytes" type="PackedByteArray"> - </argument> - <argument index="1" name="allow_objects" type="bool" default="false"> - </argument> - <description> - Decodes a byte array back to a value. When [code]allow_objects[/code] is [code]true[/code] decoding objects is allowed. - [b]WARNING:[/b] Deserialized object can contain code which gets executed. Do not use this option if the serialized object comes from untrusted sources to avoid potential security threats (remote code execution). - </description> - </method> - <method name="cartesian2polar"> - <return type="Vector2"> - </return> - <argument index="0" name="x" type="float"> - </argument> - <argument index="1" name="y" type="float"> - </argument> - <description> - Converts a 2D point expressed in the cartesian coordinate system (X and Y axis) to the polar coordinate system (a distance from the origin and an angle). - </description> - </method> - <method name="ceil"> - <return type="float"> - </return> - <argument index="0" name="s" type="float"> - </argument> - <description> - Rounds [code]s[/code] upward (towards positive infinity), returning the smallest whole number that is not less than [code]s[/code]. - [codeblock] - i = ceil(1.45) # i is 2 - i = ceil(1.001) # i is 2 - [/codeblock] - See also [method floor], [method round], and [method stepify]. - </description> - </method> <method name="char"> <return type="String"> </return> - <argument index="0" name="code" type="int"> + <argument index="0" name="char" type="int"> </argument> <description> Returns a character as a String of the given Unicode code point (which is compatible with ASCII code). @@ -184,29 +65,6 @@ a = char(65 + 32) # a is "a" a = char(8364) # a is "€" [/codeblock] - This is the inverse of [method ord]. - </description> - </method> - <method name="clamp"> - <return type="float"> - </return> - <argument index="0" name="value" type="float"> - </argument> - <argument index="1" name="min" type="float"> - </argument> - <argument index="2" name="max" type="float"> - </argument> - <description> - Clamps [code]value[/code] and returns a value not less than [code]min[/code] and not more than [code]max[/code]. - [codeblock] - speed = 1000 - # a is 20 - a = clamp(speed, 1, 20) - - speed = -10 - # a is 1 - a = clamp(speed, 1, 20) - [/codeblock] </description> </method> <method name="convert"> @@ -228,163 +86,13 @@ [/codeblock] </description> </method> - <method name="cos"> - <return type="float"> - </return> - <argument index="0" name="s" type="float"> - </argument> - <description> - Returns the cosine of angle [code]s[/code] in radians. - [codeblock] - # Prints 1 then -1 - print(cos(PI * 2)) - print(cos(PI)) - [/codeblock] - </description> - </method> - <method name="cosh"> - <return type="float"> - </return> - <argument index="0" name="s" type="float"> - </argument> - <description> - Returns the hyperbolic cosine of [code]s[/code] in radians. - [codeblock] - # Prints 1.543081 - print(cosh(1)) - [/codeblock] - </description> - </method> - <method name="db2linear"> - <return type="float"> - </return> - <argument index="0" name="db" type="float"> - </argument> - <description> - Converts from decibels to linear energy (audio). - </description> - </method> - <method name="dectime"> - <return type="float"> - </return> - <argument index="0" name="value" type="float"> - </argument> - <argument index="1" name="amount" type="float"> - </argument> - <argument index="2" name="step" type="float"> - </argument> - <description> - Returns the result of [code]value[/code] decreased by [code]step[/code] * [code]amount[/code]. - [codeblock] - # a = 59 - a = dectime(60, 10, 0.1)) - [/codeblock] - </description> - </method> - <method name="deg2rad"> - <return type="float"> - </return> - <argument index="0" name="deg" type="float"> - </argument> - <description> - Converts an angle expressed in degrees to radians. - [codeblock] - # r is 3.141593 - r = deg2rad(180) - [/codeblock] - </description> - </method> <method name="dict2inst"> <return type="Object"> </return> - <argument index="0" name="dict" type="Dictionary"> - </argument> - <description> - Converts a previously converted instance to a dictionary, back into an instance. Useful for deserializing. - </description> - </method> - <method name="ease"> - <return type="float"> - </return> - <argument index="0" name="s" type="float"> - </argument> - <argument index="1" name="curve" type="float"> + <argument index="0" name="dictionary" type="Dictionary"> </argument> <description> - Easing function, based on exponent. The curve values are: 0 is constant, 1 is linear, 0 to 1 is ease-in, 1+ is ease out. Negative values are in-out/out in. - </description> - </method> - <method name="exp"> - <return type="float"> - </return> - <argument index="0" name="s" type="float"> - </argument> - <description> - The natural exponential function. It raises the mathematical constant [b]e[/b] to the power of [code]s[/code] and returns it. - [b]e[/b] has an approximate value of 2.71828, and can be obtained with [code]exp(1)[/code]. - For exponents to other bases use the method [method pow]. - [codeblock] - a = exp(2) # Approximately 7.39 - [/codeblock] - </description> - </method> - <method name="floor"> - <return type="float"> - </return> - <argument index="0" name="s" type="float"> - </argument> - <description> - Rounds [code]s[/code] downward (towards negative infinity), returning the largest whole number that is not more than [code]s[/code]. - [codeblock] - # a is 2.0 - a = floor(2.99) - # a is -3.0 - a = floor(-2.99) - [/codeblock] - See also [method ceil], [method round], and [method stepify]. - [b]Note:[/b] This method returns a float. If you need an integer, you can use [code]int(s)[/code] directly. - </description> - </method> - <method name="fmod"> - <return type="float"> - </return> - <argument index="0" name="a" type="float"> - </argument> - <argument index="1" name="b" type="float"> - </argument> - <description> - Returns the floating-point remainder of [code]a/b[/code], keeping the sign of [code]a[/code]. - [codeblock] - # Remainder is 1.5 - var remainder = fmod(7, 5.5) - [/codeblock] - For the integer remainder operation, use the % operator. - </description> - </method> - <method name="fposmod"> - <return type="float"> - </return> - <argument index="0" name="a" type="float"> - </argument> - <argument index="1" name="b" type="float"> - </argument> - <description> - Returns the floating-point modulus of [code]a/b[/code] that wraps equally in positive and negative. - [codeblock] - for i in 7: - var x = 0.5 * i - 1.5 - print("%4.1f %4.1f %4.1f" % [x, fmod(x, 1.5), fposmod(x, 1.5)]) - [/codeblock] - Produces: - [codeblock] - -1.5 -0.0 0.0 - -1.0 -1.0 0.5 - -0.5 -0.5 1.0 - 0.0 0.0 0.0 - 0.5 0.5 0.5 - 1.0 1.0 1.0 - 1.5 0.0 0.0 - [/codeblock] + Converts a dictionary (previously created with [method inst2dict]) back to an instance. Useful for deserializing. </description> </method> <method name="get_stack"> @@ -408,22 +116,10 @@ [/codeblock] </description> </method> - <method name="hash"> - <return type="int"> - </return> - <argument index="0" name="var" type="Variant"> - </argument> - <description> - Returns the integer hash of the variable passed. - [codeblock] - print(hash("a")) # Prints 177670 - [/codeblock] - </description> - </method> <method name="inst2dict"> <return type="Dictionary"> </return> - <argument index="0" name="inst" type="Object"> + <argument index="0" name="instance" type="Object"> </argument> <description> Returns the passed instance converted to a dictionary (useful for serializing). @@ -441,92 +137,6 @@ [/codeblock] </description> </method> - <method name="instance_from_id"> - <return type="Object"> - </return> - <argument index="0" name="instance_id" type="int"> - </argument> - <description> - Returns the Object that corresponds to [code]instance_id[/code]. All Objects have a unique instance ID. - [codeblock] - var foo = "bar" - func _ready(): - var id = get_instance_id() - var inst = instance_from_id(id) - print(inst.foo) # Prints bar - [/codeblock] - </description> - </method> - <method name="inverse_lerp"> - <return type="float"> - </return> - <argument index="0" name="from" type="float"> - </argument> - <argument index="1" name="to" type="float"> - </argument> - <argument index="2" name="weight" type="float"> - </argument> - <description> - Returns a normalized value considering the given range. This is the opposite of [method lerp]. - [codeblock] - var middle = lerp(20, 30, 0.75) - # `middle` is now 27.5. - # Now, we pretend to have forgotten the original ratio and want to get it back. - var ratio = inverse_lerp(20, 30, 27.5) - # `ratio` is now 0.75. - [/codeblock] - </description> - </method> - <method name="is_equal_approx"> - <return type="bool"> - </return> - <argument index="0" name="a" type="float"> - </argument> - <argument index="1" name="b" type="float"> - </argument> - <description> - Returns [code]true[/code] if [code]a[/code] and [code]b[/code] are approximately equal to each other. - Here, approximately equal means that [code]a[/code] and [code]b[/code] are within a small internal epsilon of each other, which scales with the magnitude of the numbers. - Infinity values of the same sign are considered equal. - </description> - </method> - <method name="is_inf"> - <return type="bool"> - </return> - <argument index="0" name="s" type="float"> - </argument> - <description> - Returns whether [code]s[/code] is an infinity value (either positive infinity or negative infinity). - </description> - </method> - <method name="is_instance_valid"> - <return type="bool"> - </return> - <argument index="0" name="instance" type="Object"> - </argument> - <description> - Returns whether [code]instance[/code] is a valid object (e.g. has not been deleted from memory). - </description> - </method> - <method name="is_nan"> - <return type="bool"> - </return> - <argument index="0" name="s" type="float"> - </argument> - <description> - Returns whether [code]s[/code] is a NaN ("Not a Number" or invalid) value. - </description> - </method> - <method name="is_zero_approx"> - <return type="bool"> - </return> - <argument index="0" name="s" type="float"> - </argument> - <description> - Returns [code]true[/code] if [code]s[/code] is zero or almost zero. - This method is faster than using [method is_equal_approx] with one value as zero. - </description> - </method> <method name="len"> <return type="int"> </return> @@ -541,63 +151,6 @@ [/codeblock] </description> </method> - <method name="lerp"> - <return type="Variant"> - </return> - <argument index="0" name="from" type="Variant"> - </argument> - <argument index="1" name="to" type="Variant"> - </argument> - <argument index="2" name="weight" type="float"> - </argument> - <description> - Linearly interpolates between two values by a normalized value. This is the opposite of [method inverse_lerp]. - If the [code]from[/code] and [code]to[/code] arguments are of type [int] or [float], the return value is a [float]. - If both are of the same vector type ([Vector2], [Vector3] or [Color]), the return value will be of the same type ([code]lerp[/code] then calls the vector type's [code]lerp[/code] method). - [codeblock] - lerp(0, 4, 0.75) # Returns 3.0 - lerp(Vector2(1, 5), Vector2(3, 2), 0.5) # Returns Vector2(2, 3.5) - [/codeblock] - </description> - </method> - <method name="lerp_angle"> - <return type="float"> - </return> - <argument index="0" name="from" type="float"> - </argument> - <argument index="1" name="to" type="float"> - </argument> - <argument index="2" name="weight" type="float"> - </argument> - <description> - Linearly interpolates between two angles (in radians) by a normalized value. - Similar to [method lerp], but interpolates correctly when the angles wrap around [constant @GDScript.TAU]. - [codeblock] - extends Sprite - var elapsed = 0.0 - func _process(delta): - var min_angle = deg2rad(0.0) - var max_angle = deg2rad(90.0) - rotation = lerp_angle(min_angle, max_angle, elapsed) - elapsed += delta - [/codeblock] - </description> - </method> - <method name="linear2db"> - <return type="float"> - </return> - <argument index="0" name="nrg" type="float"> - </argument> - <description> - Converts from linear energy to decibels (audio). This can be used to implement volume sliders that behave as expected (since volume isn't linear). Example: - [codeblock] - # "Slider" refers to a node that inherits Range such as HSlider or VSlider. - # Its range must be configured to go from 0 to 1. - # Change the bus name if you'd like to change the volume of a specific bus only. - AudioServer.set_bus_volume_db(AudioServer.get_bus_index("Master"), linear2db($Slider.value)) - [/codeblock] - </description> - </method> <method name="load"> <return type="Resource"> </return> @@ -614,172 +167,6 @@ This method is a simplified version of [method ResourceLoader.load], which can be used for more advanced scenarios. </description> </method> - <method name="log"> - <return type="float"> - </return> - <argument index="0" name="s" type="float"> - </argument> - <description> - Natural logarithm. The amount of time needed to reach a certain level of continuous growth. - [b]Note:[/b] This is not the same as the "log" function on most calculators, which uses a base 10 logarithm. - [codeblock] - log(10) # Returns 2.302585 - [/codeblock] - [b]Note:[/b] The logarithm of [code]0[/code] returns [code]-inf[/code], while negative values return [code]-nan[/code]. - </description> - </method> - <method name="max"> - <return type="float"> - </return> - <argument index="0" name="a" type="float"> - </argument> - <argument index="1" name="b" type="float"> - </argument> - <description> - Returns the maximum of two values. - [codeblock] - max(1, 2) # Returns 2 - max(-3.99, -4) # Returns -3.99 - [/codeblock] - </description> - </method> - <method name="min"> - <return type="float"> - </return> - <argument index="0" name="a" type="float"> - </argument> - <argument index="1" name="b" type="float"> - </argument> - <description> - Returns the minimum of two values. - [codeblock] - min(1, 2) # Returns 1 - min(-3.99, -4) # Returns -4 - [/codeblock] - </description> - </method> - <method name="move_toward"> - <return type="float"> - </return> - <argument index="0" name="from" type="float"> - </argument> - <argument index="1" name="to" type="float"> - </argument> - <argument index="2" name="delta" type="float"> - </argument> - <description> - Moves [code]from[/code] toward [code]to[/code] by the [code]delta[/code] value. - Use a negative [code]delta[/code] value to move away. - [codeblock] - move_toward(5, 10, 4) # Returns 9 - move_toward(10, 5, 4) # Returns 6 - move_toward(10, 5, -1.5) # Returns 11.5 - [/codeblock] - </description> - </method> - <method name="nearest_po2"> - <return type="int"> - </return> - <argument index="0" name="value" type="int"> - </argument> - <description> - Returns the nearest equal or larger power of 2 for integer [code]value[/code]. - In other words, returns the smallest value [code]a[/code] where [code]a = pow(2, n)[/code] such that [code]value <= a[/code] for some non-negative integer [code]n[/code]. - [codeblock] - nearest_po2(3) # Returns 4 - nearest_po2(4) # Returns 4 - nearest_po2(5) # Returns 8 - - nearest_po2(0) # Returns 0 (this may not be what you expect) - nearest_po2(-1) # Returns 0 (this may not be what you expect) - [/codeblock] - [b]WARNING:[/b] Due to the way it is implemented, this function returns [code]0[/code] rather than [code]1[/code] for non-positive values of [code]value[/code] (in reality, 1 is the smallest integer power of 2). - </description> - </method> - <method name="ord"> - <return type="int"> - </return> - <argument index="0" name="char" type="String"> - </argument> - <description> - Returns an integer representing the Unicode code point of the given Unicode character [code]char[/code]. - [codeblock] - a = ord("A") # a is 65 - a = ord("a") # a is 97 - a = ord("€") # a is 8364 - [/codeblock] - This is the inverse of [method char]. - </description> - </method> - <method name="parse_json"> - <return type="Variant"> - </return> - <argument index="0" name="json" type="String"> - </argument> - <description> - Parse JSON text to a Variant. (Use [method typeof] to check if the Variant's type is what you expect.) - [b]Note:[/b] The JSON specification does not define integer or float types, but only a [i]number[/i] type. Therefore, parsing a JSON text will convert all numerical values to [float] types. - [b]Note:[/b] JSON objects do not preserve key order like Godot dictionaries, thus, you should not rely on keys being in a certain order if a dictionary is constructed from JSON. In contrast, JSON arrays retain the order of their elements: - [codeblock] - var p = JSON.parse('["hello", "world", "!"]') - if typeof(p.result) == TYPE_ARRAY: - print(p.result[0]) # Prints "hello" - else: - push_error("Unexpected results.") - [/codeblock] - See also [JSON] for an alternative way to parse JSON text. - </description> - </method> - <method name="polar2cartesian"> - <return type="Vector2"> - </return> - <argument index="0" name="r" type="float"> - </argument> - <argument index="1" name="th" type="float"> - </argument> - <description> - Converts a 2D point expressed in the polar coordinate system (a distance from the origin [code]r[/code] and an angle [code]th[/code]) to the cartesian coordinate system (X and Y axis). - </description> - </method> - <method name="posmod"> - <return type="int"> - </return> - <argument index="0" name="a" type="int"> - </argument> - <argument index="1" name="b" type="int"> - </argument> - <description> - Returns the integer modulus of [code]a/b[/code] that wraps equally in positive and negative. - [codeblock] - for i in range(-3, 4): - print("%2d %2d %2d" % [i, i % 3, posmod(i, 3)]) - [/codeblock] - Produces: - [codeblock] - -3 0 0 - -2 -2 1 - -1 -1 2 - 0 0 0 - 1 1 1 - 2 2 2 - 3 0 0 - [/codeblock] - </description> - </method> - <method name="pow"> - <return type="float"> - </return> - <argument index="0" name="base" type="float"> - </argument> - <argument index="1" name="exp" type="float"> - </argument> - <description> - Returns the result of [code]x[/code] raised to the power of [code]y[/code]. - [codeblock] - pow(2, 5) # Returns 32 - [/codeblock] - </description> - </method> <method name="preload"> <return type="Resource"> </return> @@ -794,23 +181,11 @@ [/codeblock] </description> </method> - <method name="print" qualifiers="vararg"> - <return type="void"> - </return> - <description> - Converts one or more arguments to strings in the best way possible and prints them to the console. - [codeblock] - a = [1, 2, 3] - print("a", "b", a) # Prints ab[1, 2, 3] - [/codeblock] - [b]Note:[/b] Consider using [method push_error] and [method push_warning] to print error and warning messages instead of [method print]. This distinguishes them from print messages used for debugging purposes, while also displaying a stack trace when an error or warning is printed. - </description> - </method> <method name="print_debug" qualifiers="vararg"> <return type="void"> </return> <description> - Like [method print], but prints only when used in debug mode. + Like [method @GlobalScope.print], but prints only when used in debug mode. </description> </method> <method name="print_stack"> @@ -824,158 +199,6 @@ [/codeblock] </description> </method> - <method name="printerr" qualifiers="vararg"> - <return type="void"> - </return> - <description> - Prints one or more arguments to strings in the best way possible to standard error line. - [codeblock] - printerr("prints to stderr") - [/codeblock] - </description> - </method> - <method name="printraw" qualifiers="vararg"> - <return type="void"> - </return> - <description> - Prints one or more arguments to strings in the best way possible to console. No newline is added at the end. - [codeblock] - printraw("A") - printraw("B") - # Prints AB - [/codeblock] - [b]Note:[/b] Due to limitations with Godot's built-in console, this only prints to the terminal. If you need to print in the editor, use another method, such as [method print]. - </description> - </method> - <method name="prints" qualifiers="vararg"> - <return type="void"> - </return> - <description> - Prints one or more arguments to the console with a space between each argument. - [codeblock] - prints("A", "B", "C") # Prints A B C - [/codeblock] - </description> - </method> - <method name="printt" qualifiers="vararg"> - <return type="void"> - </return> - <description> - Prints one or more arguments to the console with a tab between each argument. - [codeblock] - printt("A", "B", "C") # Prints A B C - [/codeblock] - </description> - </method> - <method name="push_error"> - <return type="void"> - </return> - <argument index="0" name="message" type="String"> - </argument> - <description> - Pushes an error message to Godot's built-in debugger and to the OS terminal. - [codeblock] - push_error("test error") # Prints "test error" to debugger and terminal as error call - [/codeblock] - [b]Note:[/b] Errors printed this way will not pause project execution. To print an error message and pause project execution in debug builds, use [code]assert(false, "test error")[/code] instead. - </description> - </method> - <method name="push_warning"> - <return type="void"> - </return> - <argument index="0" name="message" type="String"> - </argument> - <description> - Pushes a warning message to Godot's built-in debugger and to the OS terminal. - [codeblock] - push_warning("test warning") # Prints "test warning" to debugger and terminal as warning call - [/codeblock] - </description> - </method> - <method name="rad2deg"> - <return type="float"> - </return> - <argument index="0" name="rad" type="float"> - </argument> - <description> - Converts an angle expressed in radians to degrees. - [codeblock] - rad2deg(0.523599) # Returns 30 - [/codeblock] - </description> - </method> - <method name="rand_seed"> - <return type="Array"> - </return> - <argument index="0" name="seed" type="int"> - </argument> - <description> - Random from seed: pass a [code]seed[/code], and an array with both number and new seed is returned. "Seed" here refers to the internal state of the pseudo random number generator. The internal state of the current implementation is 64 bits. - </description> - </method> - <method name="randf"> - <return type="float"> - </return> - <description> - Returns a random floating point value on the interval [code][0, 1][/code]. - [codeblock] - randf() # Returns e.g. 0.375671 - [/codeblock] - </description> - </method> - <method name="randf_range"> - <return type="float"> - </return> - <argument index="0" name="from" type="float"> - </argument> - <argument index="1" name="to" type="float"> - </argument> - <description> - Random range, any floating point value between [code]from[/code] and [code]to[/code]. - [codeblock] - prints(randf_range(-10, 10), randf_range(-10, 10)) # Prints e.g. -3.844535 7.45315 - [/codeblock] - </description> - </method> - <method name="randi"> - <return type="int"> - </return> - <description> - Returns a random unsigned 32 bit integer. Use remainder to obtain a random value in the interval [code][0, N - 1][/code] (where N is smaller than 2^32). - [codeblock] - randi() # Returns random integer between 0 and 2^32 - 1 - randi() % 20 # Returns random integer between 0 and 19 - randi() % 100 # Returns random integer between 0 and 99 - randi() % 100 + 1 # Returns random integer between 1 and 100 - [/codeblock] - </description> - </method> - <method name="randi_range"> - <return type="int"> - </return> - <argument index="0" name="from" type="int"> - </argument> - <argument index="1" name="to" type="int"> - </argument> - <description> - Random range, any 32-bit integer value between [code]from[/code] and [code]to[/code] (inclusive). If [code]to[/code] is lesser than [code]from[/code] they are swapped. - [codeblock] - print(randi_range(0, 1)) # Prints 0 or 1 - print(randi_range(-10, 1000)) # Prints any number from -10 to 1000 - [/codeblock] - </description> - </method> - <method name="randomize"> - <return type="void"> - </return> - <description> - Randomizes the seed (or the internal state) of the random number generator. Current implementation reseeds using a number based on time. - [codeblock] - func _ready(): - randomize() - [/codeblock] - </description> - </method> <method name="range" qualifiers="vararg"> <return type="Array"> </return> @@ -994,158 +217,6 @@ [/codeblock] </description> </method> - <method name="range_lerp"> - <return type="float"> - </return> - <argument index="0" name="value" type="float"> - </argument> - <argument index="1" name="istart" type="float"> - </argument> - <argument index="2" name="istop" type="float"> - </argument> - <argument index="3" name="ostart" type="float"> - </argument> - <argument index="4" name="ostop" type="float"> - </argument> - <description> - Maps a [code]value[/code] from range [code][istart, istop][/code] to [code][ostart, ostop][/code]. - [codeblock] - range_lerp(75, 0, 100, -1, 1) # Returns 0.5 - [/codeblock] - </description> - </method> - <method name="round"> - <return type="float"> - </return> - <argument index="0" name="s" type="float"> - </argument> - <description> - Rounds [code]s[/code] to the nearest whole number, with halfway cases rounded away from zero. - [codeblock] - round(2.6) # Returns 3 - [/codeblock] - See also [method floor], [method ceil], and [method stepify]. - </description> - </method> - <method name="seed"> - <return type="void"> - </return> - <argument index="0" name="seed" type="int"> - </argument> - <description> - Sets seed for the random number generator. - [codeblock] - my_seed = "Godot Rocks" - seed(my_seed.hash()) - [/codeblock] - </description> - </method> - <method name="sign"> - <return type="float"> - </return> - <argument index="0" name="s" type="float"> - </argument> - <description> - Returns the sign of [code]s[/code]: -1 or 1. Returns 0 if [code]s[/code] is 0. - [codeblock] - sign(-6) # Returns -1 - sign(0) # Returns 0 - sign(6) # Returns 1 - [/codeblock] - </description> - </method> - <method name="sin"> - <return type="float"> - </return> - <argument index="0" name="s" type="float"> - </argument> - <description> - Returns the sine of angle [code]s[/code] in radians. - [codeblock] - sin(0.523599) # Returns 0.5 - [/codeblock] - </description> - </method> - <method name="sinh"> - <return type="float"> - </return> - <argument index="0" name="s" type="float"> - </argument> - <description> - Returns the hyperbolic sine of [code]s[/code]. - [codeblock] - a = log(2.0) # Returns 0.693147 - sinh(a) # Returns 0.75 - [/codeblock] - </description> - </method> - <method name="smoothstep"> - <return type="float"> - </return> - <argument index="0" name="from" type="float"> - </argument> - <argument index="1" name="to" type="float"> - </argument> - <argument index="2" name="s" type="float"> - </argument> - <description> - Returns the result of smoothly interpolating the value of [code]s[/code] between [code]0[/code] and [code]1[/code], based on the where [code]s[/code] lies with respect to the edges [code]from[/code] and [code]to[/code]. - The return value is [code]0[/code] if [code]s <= from[/code], and [code]1[/code] if [code]s >= to[/code]. If [code]s[/code] lies between [code]from[/code] and [code]to[/code], the returned value follows an S-shaped curve that maps [code]s[/code] between [code]0[/code] and [code]1[/code]. - This S-shaped curve is the cubic Hermite interpolator, given by [code]f(s) = 3*s^2 - 2*s^3[/code]. - [codeblock] - smoothstep(0, 2, -5.0) # Returns 0.0 - smoothstep(0, 2, 0.5) # Returns 0.15625 - smoothstep(0, 2, 1.0) # Returns 0.5 - smoothstep(0, 2, 2.0) # Returns 1.0 - [/codeblock] - </description> - </method> - <method name="sqrt"> - <return type="float"> - </return> - <argument index="0" name="s" type="float"> - </argument> - <description> - Returns the square root of [code]s[/code], where [code]s[/code] is a non-negative number. - [codeblock] - sqrt(9) # Returns 3 - [/codeblock] - [b]Note:[/b]Negative values of [code]s[/code] return NaN. If you need negative inputs, use [code]System.Numerics.Complex[/code] in C#. - </description> - </method> - <method name="step_decimals"> - <return type="int"> - </return> - <argument index="0" name="step" type="float"> - </argument> - <description> - Returns the position of the first non-zero digit, after the decimal point. Note that the maximum return value is 10, which is a design decision in the implementation. - [codeblock] - # n is 0 - n = step_decimals(5) - # n is 4 - n = step_decimals(1.0005) - # n is 9 - n = step_decimals(0.000000005) - [/codeblock] - </description> - </method> - <method name="stepify"> - <return type="float"> - </return> - <argument index="0" name="s" type="float"> - </argument> - <argument index="1" name="step" type="float"> - </argument> - <description> - Snaps float value [code]s[/code] to a given [code]step[/code]. This can also be used to round a floating point number to an arbitrary number of decimals. - [codeblock] - stepify(100, 32) # Returns 96 - stepify(3.14159, 0.01) # Returns 3.14 - [/codeblock] - See also [method ceil], [method floor], and [method round]. - </description> - </method> <method name="str" qualifiers="vararg"> <return type="String"> </return> @@ -1159,200 +230,12 @@ [/codeblock] </description> </method> - <method name="str2var"> - <return type="Variant"> - </return> - <argument index="0" name="string" type="String"> - </argument> - <description> - Converts a formatted string that was returned by [method var2str] to the original value. - [codeblock] - a = '{ "a": 1, "b": 2 }' - b = str2var(a) - print(b["a"]) # Prints 1 - [/codeblock] - </description> - </method> - <method name="tan"> - <return type="float"> - </return> - <argument index="0" name="s" type="float"> - </argument> - <description> - Returns the tangent of angle [code]s[/code] in radians. - [codeblock] - tan(deg2rad(45)) # Returns 1 - [/codeblock] - </description> - </method> - <method name="tanh"> - <return type="float"> - </return> - <argument index="0" name="s" type="float"> - </argument> - <description> - Returns the hyperbolic tangent of [code]s[/code]. - [codeblock] - a = log(2.0) # Returns 0.693147 - tanh(a) # Returns 0.6 - [/codeblock] - </description> - </method> - <method name="to_json"> - <return type="String"> - </return> - <argument index="0" name="var" type="Variant"> - </argument> - <description> - Converts a [Variant] [code]var[/code] to JSON text and return the result. Useful for serializing data to store or send over the network. - [codeblock] - # Both numbers below are integers. - a = { "a": 1, "b": 2 } - b = to_json(a) - print(b) # {"a":1, "b":2} - # Both numbers above are floats, even if they display without any decimal places. - [/codeblock] - [b]Note:[/b] The JSON specification does not define integer or float types, but only a [i]number[/i] type. Therefore, converting a [Variant] to JSON text will convert all numerical values to [float] types. - See also [JSON] for an alternative way to convert a [Variant] to JSON text. - </description> - </method> <method name="type_exists"> <return type="bool"> </return> - <argument index="0" name="type" type="String"> - </argument> - <description> - Returns whether the given class exists in [ClassDB]. - [codeblock] - type_exists("Sprite2D") # Returns true - type_exists("Variant") # Returns false - [/codeblock] - </description> - </method> - <method name="typeof"> - <return type="int"> - </return> - <argument index="0" name="what" type="Variant"> - </argument> - <description> - Returns the internal type of the given Variant object, using the [enum Variant.Type] values. - [codeblock] - p = parse_json('["a", "b", "c"]') - if typeof(p) == TYPE_ARRAY: - print(p[0]) # Prints a - else: - print("unexpected results") - [/codeblock] - </description> - </method> - <method name="validate_json"> - <return type="String"> - </return> - <argument index="0" name="json" type="String"> - </argument> - <description> - Checks that [code]json[/code] is valid JSON data. Returns an empty string if valid, or an error message otherwise. - [codeblock] - j = to_json([1, 2, 3]) - v = validate_json(j) - if not v: - print("Valid JSON.") - else: - push_error("Invalid JSON: " + v) - [/codeblock] - </description> - </method> - <method name="var2bytes"> - <return type="PackedByteArray"> - </return> - <argument index="0" name="var" type="Variant"> - </argument> - <argument index="1" name="full_objects" type="bool" default="false"> - </argument> - <description> - Encodes a variable value to a byte array. When [code]full_objects[/code] is [code]true[/code] encoding objects is allowed (and can potentially include code). - </description> - </method> - <method name="var2str"> - <return type="String"> - </return> - <argument index="0" name="var" type="Variant"> - </argument> - <description> - Converts a Variant [code]var[/code] to a formatted string that can later be parsed using [method str2var]. - [codeblock] - a = { "a": 1, "b": 2 } - print(var2str(a)) - [/codeblock] - prints - [codeblock] - { - "a": 1, - "b": 2 - } - [/codeblock] - </description> - </method> - <method name="weakref"> - <return type="WeakRef"> - </return> - <argument index="0" name="obj" type="Object"> - </argument> - <description> - Returns a weak reference to an object. - A weak reference to an object is not enough to keep the object alive: when the only remaining references to a referent are weak references, garbage collection is free to destroy the referent and reuse its memory for something else. However, until the object is actually destroyed the weak reference may return the object even if there are no strong references to it. - </description> - </method> - <method name="wrapf"> - <return type="float"> - </return> - <argument index="0" name="value" type="float"> - </argument> - <argument index="1" name="min" type="float"> - </argument> - <argument index="2" name="max" type="float"> + <argument index="0" name="type" type="StringName"> </argument> <description> - Wraps float [code]value[/code] between [code]min[/code] and [code]max[/code]. - Usable for creating loop-alike behavior or infinite surfaces. - [codeblock] - # Infinite loop between 5.0 and 9.9 - value = wrapf(value + 0.1, 5.0, 10.0) - [/codeblock] - [codeblock] - # Infinite rotation (in radians) - angle = wrapf(angle + 0.1, 0.0, TAU) - [/codeblock] - [codeblock] - # Infinite rotation (in radians) - angle = wrapf(angle + 0.1, -PI, PI) - [/codeblock] - [b]Note:[/b] If [code]min[/code] is [code]0[/code], this is equivalent to [method fposmod], so prefer using that instead. - [code]wrapf[/code] is more flexible than using the [method fposmod] approach by giving the user control over the minimum value. - </description> - </method> - <method name="wrapi"> - <return type="int"> - </return> - <argument index="0" name="value" type="int"> - </argument> - <argument index="1" name="min" type="int"> - </argument> - <argument index="2" name="max" type="int"> - </argument> - <description> - Wraps integer [code]value[/code] between [code]min[/code] and [code]max[/code]. - Usable for creating loop-alike behavior or infinite surfaces. - [codeblock] - # Infinite loop between 5 and 9 - frame = wrapi(frame + 1, 5, 10) - [/codeblock] - [codeblock] - # result is -2 - var result = wrapi(-6, -5, -1) - [/codeblock] - [b]Note:[/b] If [code]min[/code] is [code]0[/code], this is equivalent to [method posmod], so prefer using that instead. - [code]wrapi[/code] is more flexible than using the [method posmod] approach by giving the user control over the minimum value. </description> </method> </methods> diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index 7f7410a92c..b792ff54d6 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdscript/editor/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h index 49357f3d2e..1b57cb1923 100644 --- a/modules/gdscript/editor/gdscript_highlighter.h +++ b/modules/gdscript/editor/gdscript_highlighter.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -42,7 +42,7 @@ private: Color color; String start_key; String end_key; - bool line_only; + bool line_only = false; }; Vector<ColorRegion> color_regions; Map<int, int> color_region_cache; diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp index 944ed859f5..6e930b6bf4 100644 --- a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp +++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.h b/modules/gdscript/editor/gdscript_translation_parser_plugin.h index d763df01f5..5358a77140 100644 --- a/modules/gdscript/editor/gdscript_translation_parser_plugin.h +++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 6b74abf15d..502e294275 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -231,7 +231,7 @@ void GDScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder) { } #endif -void GDScript::get_script_method_list(List<MethodInfo> *p_list) const { +void GDScript::_get_script_method_list(List<MethodInfo> *r_list, bool p_include_base) const { const GDScript *current = this; while (current) { for (const Map<StringName, GDScriptFunction *>::Element *E = current->member_functions.front(); E; E = E->next()) { @@ -239,18 +239,29 @@ void GDScript::get_script_method_list(List<MethodInfo> *p_list) const { MethodInfo mi; mi.name = E->key(); for (int i = 0; i < func->get_argument_count(); i++) { - mi.arguments.push_back(func->get_argument_type(i)); + PropertyInfo arginfo = func->get_argument_type(i); +#ifdef TOOLS_ENABLED + arginfo.name = func->get_argument_name(i); +#endif + mi.arguments.push_back(arginfo); } mi.return_val = func->get_return_type(); - p_list->push_back(mi); + r_list->push_back(mi); + } + if (!p_include_base) { + return; } current = current->_base; } } -void GDScript::get_script_property_list(List<PropertyInfo> *p_list) const { +void GDScript::get_script_method_list(List<MethodInfo> *r_list) const { + _get_script_method_list(r_list, true); +} + +void GDScript::_get_script_property_list(List<PropertyInfo> *r_list, bool p_include_base) const { const GDScript *sptr = this; List<PropertyInfo> props; @@ -269,15 +280,22 @@ void GDScript::get_script_property_list(List<PropertyInfo> *p_list) const { for (int i = 0; i < msort.size(); i++) { props.push_front(sptr->member_info[msort[i].name]); } + if (!p_include_base) { + break; + } sptr = sptr->_base; } for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { - p_list->push_back(E->get()); + r_list->push_back(E->get()); } } +void GDScript::get_script_property_list(List<PropertyInfo> *r_list) const { + _get_script_property_list(r_list, true); +} + bool GDScript::has_method(const StringName &p_method) const { return member_functions.has(p_method); } @@ -383,6 +401,183 @@ void GDScript::_update_exports_values(Map<StringName, Variant> &values, List<Pro propnames.push_back(E->get()); } } + +void GDScript::_add_doc(const DocData::ClassDoc &p_inner_class) { + if (_owner) { + _owner->_add_doc(p_inner_class); + } else { + for (int i = 0; i < docs.size(); i++) { + if (docs[i].name == p_inner_class.name) { + docs.remove(i); + break; + } + } + docs.append(p_inner_class); + } +} + +void GDScript::_clear_doc() { + docs.clear(); + doc = DocData::ClassDoc(); +} + +void GDScript::_update_doc() { + _clear_doc(); + + doc.script_path = "\"" + get_path().get_slice("://", 1) + "\""; + if (!name.is_empty()) { + doc.name = name; + } else { + doc.name = doc.script_path; + } + + if (_owner) { + doc.name = _owner->doc.name + "." + doc.name; + doc.script_path = doc.script_path + "." + doc.name; + } + + doc.is_script_doc = true; + + if (base.is_valid() && base->is_valid()) { + if (base->doc.name != String()) { + doc.inherits = base->doc.name; + } else { + doc.inherits = base->get_instance_base_type(); + } + } else if (native.is_valid()) { + doc.inherits = native->get_name(); + } + + doc.brief_description = doc_brief_description; + doc.description = doc_description; + doc.tutorials = doc_tutorials; + + for (Map<String, DocData::EnumDoc>::Element *E = doc_enums.front(); E; E = E->next()) { + if (E->value().description != "") { + doc.enums[E->key()] = E->value().description; + } + } + + List<MethodInfo> methods; + _get_script_method_list(&methods, false); + for (int i = 0; i < methods.size(); i++) { + // Ignore internal methods. + if (methods[i].name[0] == '@') { + continue; + } + + DocData::MethodDoc method_doc; + const String &class_name = methods[i].name; + if (member_functions.has(class_name)) { + GDScriptFunction *fn = member_functions[class_name]; + + // Change class name if return type is script reference. + GDScriptDataType return_type = fn->get_return_type(); + if (return_type.kind == GDScriptDataType::GDSCRIPT) { + methods[i].return_val.class_name = _get_gdscript_reference_class_name(Object::cast_to<GDScript>(return_type.script_type)); + } + + // Change class name if argumetn is script reference. + for (int j = 0; j < fn->get_argument_count(); j++) { + GDScriptDataType arg_type = fn->get_argument_type(j); + if (arg_type.kind == GDScriptDataType::GDSCRIPT) { + methods[i].arguments[j].class_name = _get_gdscript_reference_class_name(Object::cast_to<GDScript>(arg_type.script_type)); + } + } + } + if (doc_functions.has(methods[i].name)) { + DocData::method_doc_from_methodinfo(method_doc, methods[i], doc_functions[methods[i].name]); + } else { + DocData::method_doc_from_methodinfo(method_doc, methods[i], String()); + } + doc.methods.push_back(method_doc); + } + + List<PropertyInfo> props; + _get_script_property_list(&props, false); + for (int i = 0; i < props.size(); i++) { + ScriptMemberInfo scr_member_info; + scr_member_info.propinfo = props[i]; + scr_member_info.propinfo.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; + if (member_indices.has(props[i].name)) { + const MemberInfo &mi = member_indices[props[i].name]; + scr_member_info.setter = mi.setter; + scr_member_info.getter = mi.getter; + if (mi.data_type.kind == GDScriptDataType::GDSCRIPT) { + scr_member_info.propinfo.class_name = _get_gdscript_reference_class_name( + Object::cast_to<GDScript>(mi.data_type.script_type)); + } + } + if (member_default_values.has(props[i].name)) { + scr_member_info.has_default_value = true; + scr_member_info.default_value = member_default_values[props[i].name]; + } + if (doc_variables.has(props[i].name)) { + scr_member_info.doc_string = doc_variables[props[i].name]; + } + + DocData::PropertyDoc prop_doc; + DocData::property_doc_from_scriptmemberinfo(prop_doc, scr_member_info); + doc.properties.push_back(prop_doc); + } + + List<MethodInfo> signals; + _get_script_signal_list(&signals, false); + for (int i = 0; i < signals.size(); i++) { + DocData::MethodDoc signal_doc; + if (doc_signals.has(signals[i].name)) { + DocData::signal_doc_from_methodinfo(signal_doc, signals[i], signals[i].name); + } else { + DocData::signal_doc_from_methodinfo(signal_doc, signals[i], String()); + } + doc.signals.push_back(signal_doc); + } + + for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) { + if (subclasses.has(E->key())) { + continue; + } + + // Enums. + bool is_enum = false; + if (E->value().get_type() == Variant::DICTIONARY) { + if (doc_enums.has(E->key())) { + is_enum = true; + for (int i = 0; i < doc_enums[E->key()].values.size(); i++) { + doc_enums[E->key()].values.write[i].enumeration = E->key(); + doc.constants.push_back(doc_enums[E->key()].values[i]); + } + } + } + if (!is_enum && doc_enums.has("@unnamed_enums")) { + for (int i = 0; i < doc_enums["@unnamed_enums"].values.size(); i++) { + if (E->key() == doc_enums["@unnamed_enums"].values[i].name) { + is_enum = true; + DocData::ConstantDoc constant_doc; + constant_doc.enumeration = "@unnamed_enums"; + DocData::constant_doc_from_variant(constant_doc, E->key(), E->value(), doc_enums["@unnamed_enums"].values[i].description); + doc.constants.push_back(constant_doc); + break; + } + } + } + if (!is_enum) { + DocData::ConstantDoc constant_doc; + String doc_description; + if (doc_constants.has(E->key())) { + doc_description = doc_constants[E->key()]; + } + DocData::constant_doc_from_variant(constant_doc, E->key(), E->value(), doc_description); + doc.constants.push_back(constant_doc); + } + } + + for (Map<StringName, Ref<GDScript>>::Element *E = subclasses.front(); E; E = E->next()) { + E->get()->_update_doc(); + } + + _add_doc(doc); +} #endif bool GDScript::_update_exports(bool *r_err, bool p_recursive_call) { @@ -598,10 +793,10 @@ Error GDScript::reload(bool p_keep_state) { { String source_path = path; - if (source_path.empty()) { + if (source_path.is_empty()) { source_path = get_path(); } - if (!source_path.empty()) { + if (!source_path.is_empty()) { MutexLock lock(GDScriptCache::singleton->lock); if (!GDScriptCache::singleton->shallow_gdscript_cache.has(source_path)) { GDScriptCache::singleton->shallow_gdscript_cache[source_path] = this; @@ -617,7 +812,7 @@ Error GDScript::reload(bool p_keep_state) { GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), parser.get_errors().front()->get().line, "Parser Error: " + parser.get_errors().front()->get().message); } // TODO: Show all error messages. - _err_print_error("GDScript::reload", path.empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_errors().front()->get().line, ("Parse Error: " + parser.get_errors().front()->get().message).utf8().get_data(), ERR_HANDLER_SCRIPT); + _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_errors().front()->get().line, ("Parse Error: " + parser.get_errors().front()->get().message).utf8().get_data(), ERR_HANDLER_SCRIPT); ERR_FAIL_V(ERR_PARSE_ERROR); } @@ -631,7 +826,7 @@ Error GDScript::reload(bool p_keep_state) { const List<GDScriptParser::ParserError>::Element *e = parser.get_errors().front(); while (e != nullptr) { - _err_print_error("GDScript::reload", path.empty() ? "built-in" : (const char *)path.utf8().get_data(), e->get().line, ("Parse Error: " + e->get().message).utf8().get_data(), ERR_HANDLER_SCRIPT); + _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), e->get().line, ("Parse Error: " + e->get().message).utf8().get_data(), ERR_HANDLER_SCRIPT); e = e->next(); } ERR_FAIL_V(ERR_PARSE_ERROR); @@ -642,12 +837,16 @@ Error GDScript::reload(bool p_keep_state) { GDScriptCompiler compiler; err = compiler.compile(&parser, this, p_keep_state); +#ifdef TOOLS_ENABLED + _update_doc(); +#endif + if (err) { if (can_run) { if (EngineDebugger::is_active()) { GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), compiler.get_error_line(), "Parser Error: " + compiler.get_error()); } - _err_print_error("GDScript::reload", path.empty() ? "built-in" : (const char *)path.utf8().get_data(), compiler.get_error_line(), ("Compile Error: " + compiler.get_error()).utf8().get_data(), ERR_HANDLER_SCRIPT); + _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), compiler.get_error_line(), ("Compile Error: " + compiler.get_error()).utf8().get_data(), ERR_HANDLER_SCRIPT); ERR_FAIL_V(ERR_COMPILATION_FAILED); } else { return err; @@ -915,7 +1114,7 @@ bool GDScript::has_script_signal(const StringName &p_signal) const { return false; } -void GDScript::get_script_signal_list(List<MethodInfo> *r_signals) const { +void GDScript::_get_script_signal_list(List<MethodInfo> *r_list, bool p_include_base) const { for (const Map<StringName, Vector<StringName>>::Element *E = _signals.front(); E; E = E->next()) { MethodInfo mi; mi.name = E->key(); @@ -924,20 +1123,43 @@ void GDScript::get_script_signal_list(List<MethodInfo> *r_signals) const { arg.name = E->get()[i]; mi.arguments.push_back(arg); } - r_signals->push_back(mi); + r_list->push_back(mi); + } + + if (!p_include_base) { + return; } if (base.is_valid()) { - base->get_script_signal_list(r_signals); + base->get_script_signal_list(r_list); } #ifdef TOOLS_ENABLED else if (base_cache.is_valid()) { - base_cache->get_script_signal_list(r_signals); + base_cache->get_script_signal_list(r_list); } #endif } +void GDScript::get_script_signal_list(List<MethodInfo> *r_signals) const { + _get_script_signal_list(r_signals, true); +} + +String GDScript::_get_gdscript_reference_class_name(const GDScript *p_gdscript) { + ERR_FAIL_NULL_V(p_gdscript, String()); + + String class_name; + while (p_gdscript) { + if (class_name == "") { + class_name = p_gdscript->get_script_class_name(); + } else { + class_name = p_gdscript->get_script_class_name() + "." + class_name; + } + p_gdscript = p_gdscript->_owner; + } + return class_name; +} + GDScript::GDScript() : script_list(this) { valid = false; @@ -1065,6 +1287,13 @@ GDScript::~GDScript() { _save_orphaned_subclasses(); +#ifdef TOOLS_ENABLED + // Clearing inner class doc, script doc only cleared when the script source deleted. + if (_owner) { + _clear_doc(); + } +#endif + #ifdef DEBUG_ENABLED { MutexLock lock(GDScriptLanguage::get_singleton()->lock); @@ -1893,8 +2122,11 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const { w++; } - for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) { - p_words->push_back(GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i))); + List<StringName> functions; + GDScriptUtilityFunctions::get_function_list(&functions); + + for (const List<StringName>::Element *E = functions.front(); E; E = E->next()) { + p_words->push_back(String(E->get())); } } @@ -1919,7 +2151,7 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b if (err == OK) { const GDScriptParser::ClassNode *c = parser.get_tree(); if (r_icon_path) { - if (c->icon_path.empty() || c->icon_path.is_abs_path()) { + if (c->icon_path.is_empty() || c->icon_path.is_abs_path()) { *r_icon_path = c->icon_path; } else if (c->icon_path.is_rel_path()) { *r_icon_path = p_path.get_base_dir().plus_file(c->icon_path).simplify_path(); @@ -1931,7 +2163,7 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b GDScriptParser subparser; while (subclass) { if (subclass->extends_used) { - if (!subclass->extends_path.empty()) { + if (!subclass->extends_path.is_empty()) { if (subclass->extends.size() == 0) { get_global_class_name(subclass->extends_path, r_base_type); subclass = nullptr; @@ -1945,7 +2177,7 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b } String subsource = subfile->get_as_utf8_string(); - if (subsource.empty()) { + if (subsource.is_empty()) { break; } String subpath = subclass->extends_path; @@ -2142,7 +2374,7 @@ void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<S ERR_FAIL_COND_MSG(!file, "Cannot open file '" + p_path + "'."); String source = file->get_as_utf8_string(); - if (source.empty()) { + if (source.is_empty()) { return; } diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index b69a6e39c0..37f01b2571 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,6 +33,7 @@ #include "core/debugger/engine_debugger.h" #include "core/debugger/script_debugger.h" +#include "core/doc_data.h" #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" #include "core/object/script_language.h" @@ -71,8 +72,8 @@ class GDScript : public Script { friend class GDScriptFunction; friend class GDScriptAnalyzer; friend class GDScriptCompiler; - friend class GDScriptFunctions; friend class GDScriptLanguage; + friend struct GDScriptUtilityFunctionsDefinitions; Ref<GDScriptNativeClass> native; Ref<GDScript> base; @@ -91,9 +92,7 @@ class GDScript : public Script { #ifdef TOOLS_ENABLED Map<StringName, int> member_lines; - Map<StringName, Variant> member_default_values; - List<PropertyInfo> members_cache; Map<StringName, Variant> member_default_values_cache; Ref<GDScript> base_cache; @@ -102,6 +101,20 @@ class GDScript : public Script { bool placeholder_fallback_enabled; void _update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames); + DocData::ClassDoc doc; + Vector<DocData::ClassDoc> docs; + String doc_brief_description; + String doc_description; + Vector<DocData::TutorialDoc> doc_tutorials; + Map<String, String> doc_functions; + Map<String, String> doc_variables; + Map<String, String> doc_constants; + Map<String, String> doc_signals; + Map<String, DocData::EnumDoc> doc_enums; + void _clear_doc(); + void _update_doc(); + void _add_doc(const DocData::ClassDoc &p_inner_class); + #endif Map<StringName, PropertyInfo> member_info; @@ -141,6 +154,13 @@ class GDScript : public Script { void _save_orphaned_subclasses(); void _init_rpc_methods_properties(); + void _get_script_property_list(List<PropertyInfo> *r_list, bool p_include_base) const; + void _get_script_method_list(List<MethodInfo> *r_list, bool p_include_base) const; + void _get_script_signal_list(List<MethodInfo> *r_list, bool p_include_base) const; + + // This method will map the class name from "Reference" to "MyClass.InnerClass". + static String _get_gdscript_reference_class_name(const GDScript *p_gdscript); + protected: bool _get(const StringName &p_name, Variant &r_ret) const; bool _set(const StringName &p_name, const Variant &p_value); @@ -191,6 +211,12 @@ public: virtual void set_source_code(const String &p_code) override; virtual void update_exports() override; +#ifdef TOOLS_ENABLED + virtual const Vector<DocData::ClassDoc> &get_documentation() const override { + return docs; + } +#endif // TOOLS_ENABLED + virtual Error reload(bool p_keep_state = false) override; void set_script_path(const String &p_path) { path = p_path; } //because subclasses need a path too... @@ -244,8 +270,8 @@ public: class GDScriptInstance : public ScriptInstance { friend class GDScript; friend class GDScriptFunction; - friend class GDScriptFunctions; friend class GDScriptCompiler; + friend struct GDScriptUtilityFunctionsDefinitions; ObjectID owner_id; Object *owner; @@ -444,7 +470,8 @@ public: virtual Script *create_script() const; virtual bool has_named_classes() const; virtual bool supports_builtin_mode() const; - virtual bool can_inherit_from_file() { return true; } + virtual bool supports_documentation() const; + virtual bool can_inherit_from_file() const { return true; } virtual int find_function(const String &p_function, const String &p_code) const; virtual String make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const; virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint); diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index aa2fa67ef2..69c9555a67 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,6 +37,7 @@ #include "core/os/file_access.h" #include "core/templates/hash_map.h" #include "gdscript.h" +#include "gdscript_utility_functions.h" // TODO: Move this to a central location (maybe core?). static HashMap<StringName, StringName> underscore_map; @@ -59,7 +60,7 @@ static const char *underscore_classes[] = { nullptr, }; static StringName get_real_class_name(const StringName &p_source) { - if (underscore_map.empty()) { + if (underscore_map.is_empty()) { const char **class_name = underscore_classes; while (*class_name != nullptr) { underscore_map[*class_name] = String("_") + *class_name; @@ -72,6 +73,39 @@ static StringName get_real_class_name(const StringName &p_source) { return p_source; } +static MethodInfo info_from_utility_func(const StringName &p_function) { + ERR_FAIL_COND_V(!Variant::has_utility_function(p_function), MethodInfo()); + + MethodInfo info(p_function); + + if (Variant::has_utility_function_return_value(p_function)) { + info.return_val.type = Variant::get_utility_function_return_type(p_function); + if (info.return_val.type == Variant::NIL) { + info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; + } + } + + if (Variant::is_utility_function_vararg(p_function)) { + info.flags |= METHOD_FLAG_VARARG; + } else { + for (int i = 0; i < Variant::get_utility_function_argument_count(p_function); i++) { + PropertyInfo pi; +#ifdef DEBUG_METHODS_ENABLED + pi.name = Variant::get_utility_function_argument_name(p_function, i); +#else + pi.name = "arg" + itos(i + 1); +#endif + pi.type = Variant::get_utility_function_argument_type(p_function, i); + if (pi.type == Variant::NIL) { + pi.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; + } + info.arguments.push_back(pi); + } + } + + return info; +} + void GDScriptAnalyzer::cleanup() { underscore_map.clear(); } @@ -175,7 +209,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, int extends_index = 0; - if (!p_class->extends_path.empty()) { + if (!p_class->extends_path.is_empty()) { Ref<GDScriptParserRef> parser = get_parser_for(p_class->extends_path); if (parser.is_null()) { push_error(vformat(R"(Could not resolve super class path "%s".)", p_class->extends_path), p_class); @@ -190,7 +224,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, base = parser->get_parser()->head->get_datatype(); } else { - if (p_class->extends.empty()) { + if (p_class->extends.is_empty()) { return ERR_PARSE_ERROR; } const StringName &name = p_class->extends[extends_index++]; @@ -342,7 +376,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type result.type_source = result.ANNOTATED_EXPLICIT; result.builtin_type = Variant::OBJECT; - if (p_type->type_chain.empty()) { + if (p_type->type_chain.is_empty()) { // void. result.kind = GDScriptParser::DataType::BUILTIN; result.builtin_type = Variant::NIL; @@ -860,7 +894,12 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode * parser->push_warning(p_function->parameters[i]->identifier, GDScriptWarning::UNUSED_PARAMETER, p_function->identifier->name, p_function->parameters[i]->identifier->name); } is_shadowing(p_function->parameters[i]->identifier, "function parameter"); -#endif +#endif // DEBUG_ENABLED +#ifdef TOOLS_ENABLED + if (p_function->parameters[i]->default_value && p_function->parameters[i]->default_value->is_constant) { + p_function->default_arg_values.push_back(p_function->parameters[i]->default_value->reduced_value); + } +#endif // TOOLS_ENABLED } if (p_function->identifier->name == "_init") { @@ -1176,7 +1215,10 @@ void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant void GDScriptAnalyzer::resolve_assert(GDScriptParser::AssertNode *p_assert) { reduce_expression(p_assert->condition); if (p_assert->message != nullptr) { - reduce_literal(p_assert->message); + reduce_expression(p_assert->message); + if (!p_assert->message->is_constant || p_assert->message->reduced_value.get_type() != Variant::STRING) { + push_error(R"(Expected constant string for assert error message.)", p_assert->message); + } } p_assert->set_datatype(p_assert->condition->get_datatype()); @@ -1611,7 +1653,7 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o if (p_binary_op->reduced_value.get_type() == Variant::STRING) { push_error(vformat(R"(%s in operator %s.)", p_binary_op->reduced_value, Variant::get_operator_name(p_binary_op->variant_op)), p_binary_op); } else { - push_error(vformat(R"(Invalid operands to operator %s, %s and %s.".)", + push_error(vformat(R"(Invalid operands to operator %s, %s and %s.)", Variant::get_operator_name(p_binary_op->variant_op), Variant::get_type_name(p_binary_op->left_operand->reduced_value.get_type()), Variant::get_type_name(p_binary_op->right_operand->reduced_value.get_type())), @@ -1696,7 +1738,6 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa // Call to name directly. StringName function_name = p_call->function_name; Variant::Type builtin_type = GDScriptParser::get_builtin_type(function_name); - GDScriptFunctions::Function builtin_function = GDScriptParser::get_builtin_function(function_name); if (builtin_type < Variant::VARIANT_MAX) { // Is a builtin constructor. @@ -1714,6 +1755,8 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa // Those are stored by reference so not suited for compile-time construction. // Because in this case they would be the same reference in all constructed values. case Variant::OBJECT: + case Variant::DICTIONARY: + case Variant::ARRAY: case Variant::PACKED_BYTE_ARRAY: case Variant::PACKED_INT32_ARRAY: case Variant::PACKED_INT64_ARRAY: @@ -1838,10 +1881,52 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa } p_call->set_datatype(call_type); return; - } else if (builtin_function < GDScriptFunctions::FUNC_MAX) { - MethodInfo function_info = GDScriptFunctions::get_info(builtin_function); + } else if (GDScriptUtilityFunctions::function_exists(function_name)) { + MethodInfo function_info = GDScriptUtilityFunctions::get_function_info(function_name); + + if (all_is_constant && GDScriptUtilityFunctions::is_function_constant(function_name)) { + // Can call on compilation. + Vector<const Variant *> args; + for (int i = 0; i < p_call->arguments.size(); i++) { + args.push_back(&(p_call->arguments[i]->reduced_value)); + } + + Variant value; + Callable::CallError err; + GDScriptUtilityFunctions::get_function(function_name)(&value, (const Variant **)args.ptr(), args.size(), err); + + switch (err.error) { + case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: { + PropertyInfo wrong_arg = function_info.arguments[err.argument]; + push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*", function_name, err.argument + 1, + type_from_property(wrong_arg).to_string(), p_call->arguments[err.argument]->get_datatype().to_string()), + p_call->arguments[err.argument]); + } break; + case Callable::CallError::CALL_ERROR_INVALID_METHOD: + push_error(vformat(R"(Invalid call for function "%s".)", function_name), p_call); + break; + case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: + push_error(vformat(R"*(Too many arguments for "%s()" call. Expected at most %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call); + break; + case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS: + push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call); + break; + case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL: + break; // Can't happen in a builtin constructor. + case Callable::CallError::CALL_OK: + p_call->is_constant = true; + p_call->reduced_value = value; + break; + } + } else { + validate_call_arg(function_info, p_call); + } + p_call->set_datatype(type_from_property(function_info.return_val)); + return; + } else if (Variant::has_utility_function(function_name)) { + MethodInfo function_info = info_from_utility_func(function_name); - if (all_is_constant && GDScriptFunctions::is_deterministic(builtin_function)) { + if (all_is_constant && Variant::get_utility_function_type(function_name) == Variant::UTILITY_FUNC_TYPE_MATH) { // Can call on compilation. Vector<const Variant *> args; for (int i = 0; i < p_call->arguments.size(); i++) { @@ -1850,23 +1935,23 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa Variant value; Callable::CallError err; - GDScriptFunctions::call(builtin_function, (const Variant **)args.ptr(), args.size(), value, err); + Variant::call_utility_function(function_name, &value, (const Variant **)args.ptr(), args.size(), err); switch (err.error) { case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: { PropertyInfo wrong_arg = function_info.arguments[err.argument]; - push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*", GDScriptFunctions::get_func_name(builtin_function), err.argument + 1, + push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*", function_name, err.argument + 1, type_from_property(wrong_arg).to_string(), p_call->arguments[err.argument]->get_datatype().to_string()), p_call->arguments[err.argument]); } break; case Callable::CallError::CALL_ERROR_INVALID_METHOD: - push_error(vformat(R"(Invalid call for function "%s".)", GDScriptFunctions::get_func_name(builtin_function)), p_call); + push_error(vformat(R"(Invalid call for function "%s".)", function_name), p_call); break; case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: - push_error(vformat(R"*(Too many arguments for "%s()" call. Expected at most %d but received %d.)*", GDScriptFunctions::get_func_name(builtin_function), err.expected, p_call->arguments.size()), p_call); + push_error(vformat(R"*(Too many arguments for "%s()" call. Expected at most %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call); break; case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS: - push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", GDScriptFunctions::get_func_name(builtin_function), err.expected, p_call->arguments.size()), p_call); + push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call); break; case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL: break; // Can't happen in a builtin constructor. @@ -1949,14 +2034,14 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa push_error(vformat(R"*(Name "%s" called as a function but is a "%s".)*", p_call->function_name, callee_datatype.to_string()), p_call->callee); } #ifdef DEBUG_ENABLED - } else if (!is_self) { + } else if (!is_self && !(base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::BUILTIN)) { parser->push_warning(p_call, GDScriptWarning::UNSAFE_METHOD_ACCESS, p_call->function_name, base_type.to_string()); mark_node_unsafe(p_call); #endif } } } - if (!found && is_self) { + if (!found && (is_self || (base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::BUILTIN))) { String base_name = is_self && !p_call->is_super ? "self" : base_type.to_string(); push_error(vformat(R"*(Function "%s()" not found in base %s.)*", p_call->function_name, base_name), p_call->is_super ? p_call : p_call->callee); } @@ -2380,7 +2465,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident // Not found. // Check if it's a builtin function. - if (parser->get_builtin_function(name) < GDScriptFunctions::FUNC_MAX) { + if (GDScriptUtilityFunctions::function_exists(name)) { push_error(vformat(R"(Built-in function "%s" cannot be used as an identifier.)", name), p_identifier); } else { push_error(vformat(R"(Identifier "%s" not declared in the current scope.)", name), p_identifier); @@ -3322,12 +3407,12 @@ Error GDScriptAnalyzer::resolve_inheritance() { Error GDScriptAnalyzer::resolve_interface() { resolve_class_interface(parser->head); - return parser->errors.empty() ? OK : ERR_PARSE_ERROR; + return parser->errors.is_empty() ? OK : ERR_PARSE_ERROR; } Error GDScriptAnalyzer::resolve_body() { resolve_class_body(parser->head); - return parser->errors.empty() ? OK : ERR_PARSE_ERROR; + return parser->errors.is_empty() ? OK : ERR_PARSE_ERROR; } Error GDScriptAnalyzer::resolve_program() { @@ -3342,7 +3427,7 @@ Error GDScriptAnalyzer::resolve_program() { } depended_parsers[E->get()]->raise_status(GDScriptParserRef::FULLY_SOLVED); } - return parser->errors.empty() ? OK : ERR_PARSE_ERROR; + return parser->errors.is_empty() ? OK : ERR_PARSE_ERROR; } Error GDScriptAnalyzer::analyze() { diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index 9925167856..dab5b032a3 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp index 7c20cda39c..873d2b0183 100644 --- a/modules/gdscript/gdscript_byte_codegen.cpp +++ b/modules/gdscript/gdscript_byte_codegen.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -40,10 +40,6 @@ uint32_t GDScriptByteCodeGenerator::add_parameter(const StringName &p_name, bool function->_argument_count++; function->argument_types.push_back(p_type); if (p_is_optional) { - if (function->_default_arg_count == 0) { - append(GDScriptFunction::OPCODE_JUMP_TO_DEF_ARGUMENT); - } - function->default_arguments.push_back(opcodes.size()); function->_default_arg_count++; } @@ -96,7 +92,12 @@ void GDScriptByteCodeGenerator::pop_temporary() { current_temporaries--; } -void GDScriptByteCodeGenerator::start_parameters() {} +void GDScriptByteCodeGenerator::start_parameters() { + if (function->_default_arg_count > 0) { + append(GDScriptFunction::OPCODE_JUMP_TO_DEF_ARGUMENT); + function->default_arguments.push_back(opcodes.size()); + } +} void GDScriptByteCodeGenerator::end_parameters() { function->default_arguments.invert(); @@ -167,7 +168,7 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() { } if (function->default_arguments.size()) { - function->_default_arg_count = function->default_arguments.size(); + function->_default_arg_count = function->default_arguments.size() - 1; function->_default_arg_ptr = &function->default_arguments[0]; } else { function->_default_arg_count = 0; @@ -282,6 +283,30 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() { function->_constructors_count = 0; } + if (utilities_map.size()) { + function->utilities.resize(utilities_map.size()); + function->_utilities_ptr = function->utilities.ptr(); + function->_utilities_count = utilities_map.size(); + for (const Map<Variant::ValidatedUtilityFunction, int>::Element *E = utilities_map.front(); E; E = E->next()) { + function->utilities.write[E->get()] = E->key(); + } + } else { + function->_utilities_ptr = nullptr; + function->_utilities_count = 0; + } + + if (gds_utilities_map.size()) { + function->gds_utilities.resize(gds_utilities_map.size()); + function->_gds_utilities_ptr = function->gds_utilities.ptr(); + function->_gds_utilities_count = gds_utilities_map.size(); + for (const Map<GDScriptUtilityFunctions::FunctionPtr, int>::Element *E = gds_utilities_map.front(); E; E = E->next()) { + function->gds_utilities.write[E->get()] = E->key(); + } + } else { + function->_gds_utilities_ptr = nullptr; + function->_gds_utilities_count = 0; + } + if (method_bind_map.size()) { function->methods.resize(method_bind_map.size()); function->_methods_ptr = function->methods.ptrw(); @@ -403,7 +428,7 @@ void GDScriptByteCodeGenerator::write_end_and(const Address &p_target) { patch_jump(logic_op_jump_pos2.back()->get()); logic_op_jump_pos1.pop_back(); logic_op_jump_pos2.pop_back(); - append(GDScriptFunction::OPCODE_ASSIGN_FALSE, 0); + append(GDScriptFunction::OPCODE_ASSIGN_FALSE, 1); append(p_target); } @@ -428,7 +453,7 @@ void GDScriptByteCodeGenerator::write_end_or(const Address &p_target) { // Jump away from the success condition. append(GDScriptFunction::OPCODE_JUMP, 0); append(opcodes.size() + 3); - // Here it means one of operands is false. + // Here it means one of operands is true. patch_jump(logic_op_jump_pos1.back()->get()); patch_jump(logic_op_jump_pos2.back()->get()); logic_op_jump_pos1.pop_back(); @@ -633,6 +658,11 @@ void GDScriptByteCodeGenerator::write_assign_false(const Address &p_target) { append(p_target); } +void GDScriptByteCodeGenerator::write_assign_default_parameter(const Address &p_dst, const Address &p_src) { + write_assign(p_dst, p_src); + function->default_arguments.push_back(opcodes.size()); +} + void GDScriptByteCodeGenerator::write_cast(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) { int index = 0; @@ -698,8 +728,8 @@ void GDScriptByteCodeGenerator::write_call_async(const Address &p_target, const append(p_function_name); } -void GDScriptByteCodeGenerator::write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector<Address> &p_arguments) { - append(GDScriptFunction::OPCODE_CALL_BUILT_IN, 1 + p_arguments.size()); +void GDScriptByteCodeGenerator::write_call_gdscript_utility(const Address &p_target, GDScriptUtilityFunctions::FunctionPtr p_function, const Vector<Address> &p_arguments) { + append(GDScriptFunction::OPCODE_CALL_GDSCRIPT_UTILITY, 1 + p_arguments.size()); for (int i = 0; i < p_arguments.size(); i++) { append(p_arguments[i]); } @@ -708,6 +738,41 @@ void GDScriptByteCodeGenerator::write_call_builtin(const Address &p_target, GDSc append(p_function); } +void GDScriptByteCodeGenerator::write_call_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) { + bool is_validated = true; + if (Variant::is_utility_function_vararg(p_function)) { + is_validated = true; // Vararg works fine with any argument, since they can be any type. + } else if (p_arguments.size() == Variant::get_utility_function_argument_count(p_function)) { + bool all_types_exact = true; + for (int i = 0; i < p_arguments.size(); i++) { + if (!IS_BUILTIN_TYPE(p_arguments[i], Variant::get_utility_function_argument_type(p_function, i))) { + all_types_exact = false; + break; + } + } + + is_validated = all_types_exact; + } + + if (is_validated) { + append(GDScriptFunction::OPCODE_CALL_UTILITY_VALIDATED, 1 + p_arguments.size()); + for (int i = 0; i < p_arguments.size(); i++) { + append(p_arguments[i]); + } + append(p_target); + append(p_arguments.size()); + append(Variant::get_validated_utility_function(p_function)); + } else { + append(GDScriptFunction::OPCODE_CALL_UTILITY, 1 + p_arguments.size()); + for (int i = 0; i < p_arguments.size(); i++) { + append(p_arguments[i]); + } + append(p_target); + append(p_arguments.size()); + append(p_function); + } +} + void GDScriptByteCodeGenerator::write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) { bool is_validated = false; diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h index 34d2bb6098..df1ecfff6d 100644 --- a/modules/gdscript/gdscript_byte_codegen.h +++ b/modules/gdscript/gdscript_byte_codegen.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -34,6 +34,7 @@ #include "gdscript_codegen.h" #include "gdscript_function.h" +#include "gdscript_utility_functions.h" class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { bool ended = false; @@ -76,6 +77,8 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { Map<Variant::ValidatedIndexedGetter, int> indexed_getters_map; Map<Variant::ValidatedBuiltInMethod, int> builtin_method_map; Map<Variant::ValidatedConstructor, int> constructors_map; + Map<Variant::ValidatedUtilityFunction, int> utilities_map; + Map<GDScriptUtilityFunctions::FunctionPtr, int> gds_utilities_map; Map<MethodBind *, int> method_bind_map; // Lists since these can be nested. @@ -241,6 +244,24 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { return pos; } + int get_utility_pos(const Variant::ValidatedUtilityFunction p_utility) { + if (utilities_map.has(p_utility)) { + return utilities_map[p_utility]; + } + int pos = utilities_map.size(); + utilities_map[p_utility] = pos; + return pos; + } + + int get_gds_utility_pos(const GDScriptUtilityFunctions::FunctionPtr p_gds_utility) { + if (gds_utilities_map.has(p_gds_utility)) { + return gds_utilities_map[p_gds_utility]; + } + int pos = gds_utilities_map.size(); + gds_utilities_map[p_gds_utility] = pos; + return pos; + } + int get_method_bind_pos(MethodBind *p_method) { if (method_bind_map.has(p_method)) { return method_bind_map[p_method]; @@ -346,6 +367,14 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { opcodes.push_back(get_constructor_pos(p_constructor)); } + void append(const Variant::ValidatedUtilityFunction p_utility) { + opcodes.push_back(get_utility_pos(p_utility)); + } + + void append(const GDScriptUtilityFunctions::FunctionPtr p_gds_utility) { + opcodes.push_back(get_gds_utility_pos(p_gds_utility)); + } + void append(MethodBind *p_method) { opcodes.push_back(get_method_bind_pos(p_method)); } @@ -401,11 +430,13 @@ public: virtual void write_assign(const Address &p_target, const Address &p_source) override; virtual void write_assign_true(const Address &p_target) override; virtual void write_assign_false(const Address &p_target) override; + virtual void write_assign_default_parameter(const Address &p_dst, const Address &p_src) override; virtual void write_cast(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) override; virtual void write_call(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override; virtual void write_super_call(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override; virtual void write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override; - virtual void write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector<Address> &p_arguments) override; + virtual void write_call_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) override; + virtual void write_call_gdscript_utility(const Address &p_target, GDScriptUtilityFunctions::FunctionPtr p_function, const Vector<Address> &p_arguments) override; virtual void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) override; virtual void write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) override; virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) override; diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp index 95d24a8b08..113d36be98 100644 --- a/modules/gdscript/gdscript_cache.cpp +++ b/modules/gdscript/gdscript_cache.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdscript/gdscript_cache.h b/modules/gdscript/gdscript_cache.h index 90c5884985..d1d2a2abbf 100644 --- a/modules/gdscript/gdscript_cache.h +++ b/modules/gdscript/gdscript_cache.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h index 35e326c61f..d9ad7e058e 100644 --- a/modules/gdscript/gdscript_codegen.h +++ b/modules/gdscript/gdscript_codegen.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -35,7 +35,7 @@ #include "core/string/string_name.h" #include "core/variant/variant.h" #include "gdscript_function.h" -#include "gdscript_functions.h" +#include "gdscript_utility_functions.h" class GDScriptCodeGenerator { public: @@ -122,11 +122,13 @@ public: virtual void write_assign(const Address &p_target, const Address &p_source) = 0; virtual void write_assign_true(const Address &p_target) = 0; virtual void write_assign_false(const Address &p_target) = 0; + virtual void write_assign_default_parameter(const Address &dst, const Address &src) = 0; virtual void write_cast(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) = 0; virtual void write_call(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0; virtual void write_super_call(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0; virtual void write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0; - virtual void write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector<Address> &p_arguments) = 0; + virtual void write_call_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) = 0; + virtual void write_call_gdscript_utility(const Address &p_target, GDScriptUtilityFunctions::FunctionPtr p_function, const Vector<Address> &p_arguments) = 0; virtual void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) = 0; virtual void write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) = 0; virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) = 0; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index b41dc15324..e8be310375 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,6 +33,7 @@ #include "gdscript.h" #include "gdscript_byte_codegen.h" #include "gdscript_cache.h" +#include "gdscript_utility_functions.h" bool GDScriptCompiler::_is_class_member_property(CodeGen &codegen, const StringName &p_name) { if (codegen.function_node && codegen.function_node->is_static) { @@ -98,14 +99,15 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D } break; case GDScriptParser::DataType::SCRIPT: { result.kind = GDScriptDataType::SCRIPT; - result.script_type = Ref<Script>(p_datatype.script_type).ptr(); + result.script_type_ref = Ref<Script>(p_datatype.script_type); + result.script_type = result.script_type_ref.ptr(); result.native_type = result.script_type->get_instance_base_type(); } break; case GDScriptParser::DataType::CLASS: { // Locate class by constructing the path to it and following that path GDScriptParser::ClassNode *class_type = p_datatype.class_type; if (class_type) { - if (class_type->fqcn.begins_with(main_script->path) || (!main_script->name.empty() && class_type->fqcn.begins_with(main_script->name))) { + if (class_type->fqcn.begins_with(main_script->path) || (!main_script->name.is_empty() && class_type->fqcn.begins_with(main_script->name))) { // Local class. List<StringName> names; while (class_type->outer) { @@ -124,11 +126,13 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D names.pop_back(); } result.kind = GDScriptDataType::GDSCRIPT; - result.script_type = script.ptr(); + result.script_type_ref = script; + result.script_type = result.script_type_ref.ptr(); result.native_type = script->get_instance_base_type(); } else { result.kind = GDScriptDataType::GDSCRIPT; - result.script_type = GDScriptCache::get_shallow_script(p_datatype.script_path, main_script->path).ptr(); + result.script_type_ref = GDScriptCache::get_shallow_script(p_datatype.script_path, main_script->path); + result.script_type = result.script_type_ref.ptr(); result.native_type = p_datatype.native_type; } } @@ -151,8 +155,8 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D // Only hold strong reference to the script if it's not the owner of the // element qualified with this type, to avoid cyclic references (leaks). - if (result.script_type && result.script_type != p_owner) { - result.script_type_ref = Ref<Script>(result.script_type); + if (result.script_type && result.script_type == p_owner) { + result.script_type_ref = Ref<Script>(); } return result; @@ -453,15 +457,17 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code arguments.push_back(arg); } - if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name) != Variant::VARIANT_MAX) { + if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(call->function_name) != Variant::VARIANT_MAX) { // Construct a built-in type. Variant::Type vtype = GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name); gen->write_construct(result, vtype, arguments); - } else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_function(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name) != GDScriptFunctions::FUNC_MAX) { - // Built-in function. - GDScriptFunctions::Function func = GDScriptParser::get_builtin_function(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name); - gen->write_call_builtin(result, func, arguments); + } else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && Variant::has_utility_function(call->function_name)) { + // Variant utility function. + gen->write_call_utility(result, call->function_name, arguments); + } else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptUtilityFunctions::function_exists(call->function_name)) { + // GDScript utility function. + gen->write_call_gdscript_utility(result, GDScriptUtilityFunctions::get_function(call->function_name), arguments); } else { // Regular function. const GDScriptParser::ExpressionNode *callee = call->callee; @@ -1132,7 +1138,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c // Evaluate expression type. Vector<GDScriptCodeGenerator::Address> typeof_args; typeof_args.push_back(expr_addr); - codegen.generator->write_call_builtin(result_addr, GDScriptFunctions::TYPE_OF, typeof_args); + codegen.generator->write_call_utility(result_addr, "typeof", typeof_args); // Check type equality. codegen.generator->write_binary_operator(result_addr, Variant::OP_EQUAL, p_type_addr, result_addr); @@ -1196,7 +1202,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c GDScriptCodeGenerator::Address value_length_addr = codegen.add_temporary(temp_type); Vector<GDScriptCodeGenerator::Address> len_args; len_args.push_back(p_value_addr); - codegen.generator->write_call_builtin(value_length_addr, GDScriptFunctions::LEN, len_args); + codegen.generator->write_call_gdscript_utility(value_length_addr, GDScriptUtilityFunctions::get_function("len"), len_args); // Test length compatibility. temp_type.builtin_type = Variant::BOOL; @@ -1250,7 +1256,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c // Also get type of element. Vector<GDScriptCodeGenerator::Address> typeof_args; typeof_args.push_back(element_addr); - codegen.generator->write_call_builtin(element_type_addr, GDScriptFunctions::TYPE_OF, typeof_args); + codegen.generator->write_call_utility(element_type_addr, "typeof", typeof_args); // Try the pattern inside the element. test_addr = _parse_match_pattern(codegen, r_error, p_pattern->array[i], element_addr, element_type_addr, p_previous_test, false, true); @@ -1295,7 +1301,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c GDScriptCodeGenerator::Address value_length_addr = codegen.add_temporary(temp_type); Vector<GDScriptCodeGenerator::Address> func_args; func_args.push_back(p_value_addr); - codegen.generator->write_call_builtin(value_length_addr, GDScriptFunctions::LEN, func_args); + codegen.generator->write_call_gdscript_utility(value_length_addr, GDScriptUtilityFunctions::get_function("len"), func_args); // Test length compatibility. temp_type.builtin_type = Variant::BOOL; @@ -1364,7 +1370,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c // Also get type of value. func_args.clear(); func_args.push_back(element_addr); - codegen.generator->write_call_builtin(element_type_addr, GDScriptFunctions::TYPE_OF, func_args); + codegen.generator->write_call_utility(element_type_addr, "typeof", func_args); // Try the pattern inside the value. test_addr = _parse_match_pattern(codegen, r_error, element.value_pattern, element_addr, element_type_addr, test_addr, false, true); @@ -1497,7 +1503,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui Vector<GDScriptCodeGenerator::Address> typeof_args; typeof_args.push_back(value); - gen->write_call_builtin(type, GDScriptFunctions::TYPE_OF, typeof_args); + gen->write_call_utility(type, "typeof", typeof_args); // Now we can actually start testing. // For each branch. @@ -1838,7 +1844,7 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser return error; } GDScriptCodeGenerator::Address dst_addr = codegen.parameters[parameter->identifier->name]; - codegen.generator->write_assign(dst_addr, src_addr); + codegen.generator->write_assign_default_parameter(dst_addr, src_addr); if (src_addr.mode == GDScriptCodeGenerator::Address::TEMPORARY) { codegen.generator->pop_temporary(); } @@ -1883,6 +1889,7 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser codegen.generator->set_initial_line(p_func->start_line); #ifdef TOOLS_ENABLED p_script->member_lines[func_name] = p_func->start_line; + p_script->doc_functions[func_name] = p_func->doc_description; #endif } else { codegen.generator->set_initial_line(0); @@ -1896,6 +1903,21 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser p_script->implicit_initializer = gd_function; } + if (p_func) { + // if no return statement -> return type is void not unresolved Variant + if (p_func->body->has_return) { + gd_function->return_type = _gdtype_from_datatype(p_func->get_datatype()); + } else { + gd_function->return_type = GDScriptDataType(); + gd_function->return_type.has_type = true; + gd_function->return_type.kind = GDScriptDataType::BUILTIN; + gd_function->return_type.builtin_type = Variant::NIL; + } +#ifdef TOOLS_ENABLED + gd_function->default_arg_values = p_func->default_arg_values; +#endif + } + p_script->member_functions[func_name] = gd_function; memdelete(codegen.generator); @@ -1993,6 +2015,24 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar } } +#ifdef TOOLS_ENABLED + p_script->doc_functions.clear(); + p_script->doc_variables.clear(); + p_script->doc_constants.clear(); + p_script->doc_enums.clear(); + p_script->doc_signals.clear(); + p_script->doc_tutorials.clear(); + + p_script->doc_brief_description = p_class->doc_brief_description; + p_script->doc_description = p_class->doc_description; + for (int i = 0; i < p_class->doc_tutorials.size(); i++) { + DocData::TutorialDoc td; + td.title = p_class->doc_tutorials[i].first; + td.link = p_class->doc_tutorials[i].second; + p_script->doc_tutorials.append(td); + } +#endif + p_script->native = Ref<GDScriptNativeClass>(); p_script->base = Ref<GDScript>(); p_script->_base = nullptr; @@ -2105,20 +2145,23 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar prop_info.hint = export_info.hint; prop_info.hint_string = export_info.hint_string; prop_info.usage = export_info.usage; -#ifdef TOOLS_ENABLED - if (variable->initializer != nullptr && variable->initializer->type == GDScriptParser::Node::LITERAL) { - p_script->member_default_values[name] = static_cast<const GDScriptParser::LiteralNode *>(variable->initializer)->value; - } -#endif } else { prop_info.usage = PROPERTY_USAGE_SCRIPT_VARIABLE; } +#ifdef TOOLS_ENABLED + p_script->doc_variables[name] = variable->doc_description; +#endif p_script->member_info[name] = prop_info; p_script->member_indices[name] = minfo; p_script->members.insert(name); #ifdef TOOLS_ENABLED + if (variable->initializer != nullptr && variable->initializer->is_constant) { + p_script->member_default_values[name] = variable->initializer->reduced_value; + } else { + p_script->member_default_values.erase(name); + } p_script->member_lines[name] = variable->start_line; #endif } break; @@ -2129,8 +2172,10 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar p_script->constants.insert(name, constant->initializer->reduced_value); #ifdef TOOLS_ENABLED - p_script->member_lines[name] = constant->start_line; + if (constant->doc_description != String()) { + p_script->doc_constants[name] = constant->doc_description; + } #endif } break; @@ -2141,6 +2186,15 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar p_script->constants.insert(name, enum_value.value); #ifdef TOOLS_ENABLED p_script->member_lines[name] = enum_value.identifier->start_line; + if (!p_script->doc_enums.has("@unnamed_enums")) { + p_script->doc_enums["@unnamed_enums"] = DocData::EnumDoc(); + p_script->doc_enums["@unnamed_enums"].name = "@unnamed_enums"; + } + DocData::ConstantDoc const_doc; + const_doc.name = enum_value.identifier->name; + const_doc.value = Variant(enum_value.value).operator String(); // TODO-DOC: enum value currently is int. + const_doc.description = enum_value.doc_description; + p_script->doc_enums["@unnamed_enums"].values.push_back(const_doc); #endif } break; @@ -2176,6 +2230,11 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar parameters_names.write[j] = signal->parameters[j]->identifier->name; } p_script->_signals[name] = parameters_names; +#ifdef TOOLS_ENABLED + if (!signal->doc_description.is_empty()) { + p_script->doc_signals[name] = signal->doc_description; + } +#endif } break; case GDScriptParser::ClassNode::Member::ENUM: { @@ -2192,6 +2251,16 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar p_script->constants.insert(enum_n->identifier->name, new_enum); #ifdef TOOLS_ENABLED p_script->member_lines[enum_n->identifier->name] = enum_n->start_line; + p_script->doc_enums[enum_n->identifier->name] = DocData::EnumDoc(); + p_script->doc_enums[enum_n->identifier->name].name = enum_n->identifier->name; + p_script->doc_enums[enum_n->identifier->name].description = enum_n->doc_description; + for (int j = 0; j < enum_n->values.size(); j++) { + DocData::ConstantDoc const_doc; + const_doc.name = enum_n->values[j].identifier->name; + const_doc.value = Variant(enum_n->values[j].value).operator String(); + const_doc.description = enum_n->values[j].doc_description; + p_script->doc_enums[enum_n->identifier->name].values.push_back(const_doc); + } #endif } break; default: diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h index 157c801f56..00953ad752 100644 --- a/modules/gdscript/gdscript_compiler.h +++ b/modules/gdscript/gdscript_compiler.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp index 92a44c57f8..17cb5e3c96 100644 --- a/modules/gdscript/gdscript_disassembler.cpp +++ b/modules/gdscript/gdscript_disassembler.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -34,7 +34,6 @@ #include "core/string/string_builder.h" #include "gdscript.h" -#include "gdscript_functions.h" static String _get_variant_string(const Variant &p_variant) { String txt; @@ -324,11 +323,8 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const { incr += 4; } break; case OPCODE_ASSIGN_TYPED_NATIVE: { - Variant class_name = _constants_ptr[_code_ptr[ip + 3]]; - GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(class_name.operator Object *()); - text += "assign typed native ("; - text += nc->get_name().operator String(); + text += DADDR(3); text += ") "; text += DADDR(1); text += " = "; @@ -607,13 +603,49 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const { incr = 5 + argc; } break; - case OPCODE_CALL_BUILT_IN: { - text += "call-built-in "; + case OPCODE_CALL_UTILITY: { + text += "call-utility "; + + int argc = _code_ptr[ip + 1 + instr_var_args]; + text += DADDR(1 + argc) + " = "; + + text += _global_names_ptr[_code_ptr[ip + 2 + instr_var_args]]; + text += "("; + + for (int i = 0; i < argc; i++) { + if (i > 0) + text += ", "; + text += DADDR(1 + i); + } + text += ")"; + + incr = 4 + argc; + } break; + case OPCODE_CALL_UTILITY_VALIDATED: { + text += "call-utility "; + + int argc = _code_ptr[ip + 1 + instr_var_args]; + text += DADDR(1 + argc) + " = "; + + text += "<unkown function>"; + text += "("; + + for (int i = 0; i < argc; i++) { + if (i > 0) + text += ", "; + text += DADDR(1 + i); + } + text += ")"; + + incr = 4 + argc; + } break; + case OPCODE_CALL_GDSCRIPT_UTILITY: { + text += "call-gscript-utility "; int argc = _code_ptr[ip + 1 + instr_var_args]; text += DADDR(1 + argc) + " = "; - text += GDScriptFunctions::get_func_name(GDScriptFunctions::Function(_code_ptr[ip + 2 + instr_var_args])); + text += "<unknown function>"; text += "("; for (int i = 0; i < argc; i++) { diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index a426046797..8ea51c61fb 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,6 +37,7 @@ #include "gdscript_compiler.h" #include "gdscript_parser.h" #include "gdscript_tokenizer.h" +#include "gdscript_utility_functions.h" #ifdef TOOLS_ENABLED #include "core/config/project_settings.h" @@ -122,10 +123,10 @@ static void get_function_names_recursively(const GDScriptParser::ClassNode *p_cl for (int i = 0; i < p_class->members.size(); i++) { if (p_class->members[i].type == GDScriptParser::ClassNode::Member::FUNCTION) { const GDScriptParser::FunctionNode *function = p_class->members[i].function; - r_funcs[function->start_line] = p_prefix.empty() ? String(function->identifier->name) : p_prefix + "." + String(function->identifier->name); + r_funcs[function->start_line] = p_prefix.is_empty() ? String(function->identifier->name) : p_prefix + "." + String(function->identifier->name); } else if (p_class->members[i].type == GDScriptParser::ClassNode::Member::CLASS) { String new_prefix = p_class->members[i].m_class->identifier->name; - get_function_names_recursively(p_class->members[i].m_class, p_prefix.empty() ? new_prefix : p_prefix + "." + new_prefix, r_funcs); + get_function_names_recursively(p_class->members[i].m_class, p_prefix.is_empty() ? new_prefix : p_prefix + "." + new_prefix, r_funcs); } } } @@ -193,6 +194,10 @@ bool GDScriptLanguage::supports_builtin_mode() const { return true; } +bool GDScriptLanguage::supports_documentation() const { + return true; +} + int GDScriptLanguage::find_function(const String &p_function, const String &p_code) const { GDScriptTokenizer tokenizer; tokenizer.set_source_code(p_code); @@ -407,11 +412,14 @@ void GDScriptLanguage::get_recognized_extensions(List<String> *p_extensions) con } void GDScriptLanguage::get_public_functions(List<MethodInfo> *p_functions) const { - for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) { - p_functions->push_back(GDScriptFunctions::get_info(GDScriptFunctions::Function(i))); + List<StringName> functions; + GDScriptUtilityFunctions::get_function_list(&functions); + + for (const List<StringName>::Element *E = functions.front(); E; E = E->next()) { + p_functions->push_back(GDScriptUtilityFunctions::get_function_info(E->get())); } - //not really "functions", but.. + // Not really "functions", but show in documentation. { MethodInfo mi; mi.name = "preload"; @@ -468,7 +476,7 @@ String GDScriptLanguage::make_function(const String &p_class, const String &p_na s += p_args[i].get_slice(":", 0); if (th) { String type = p_args[i].get_slice(":", 1); - if (!type.empty() && type != "var") { + if (!type.is_empty() && type != "var") { s += ": " + type; } } @@ -1026,9 +1034,12 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool _find_identifiers_in_class(p_context.current_class, p_only_functions, (!p_context.current_function || p_context.current_function->is_static), false, r_result, p_recursion_depth + 1); } - for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) { - MethodInfo function = GDScriptFunctions::get_info(GDScriptFunctions::Function(i)); - ScriptCodeCompletionOption option(String(GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i))), ScriptCodeCompletionOption::KIND_FUNCTION); + List<StringName> functions; + GDScriptUtilityFunctions::get_function_list(&functions); + + for (const List<StringName>::Element *E = functions.front(); E; E = E->next()) { + MethodInfo function = GDScriptUtilityFunctions::get_function_info(E->get()); + ScriptCodeCompletionOption option(String(E->get()), ScriptCodeCompletionOption::KIND_FUNCTION); if (function.arguments.size() || (function.flags & METHOD_FLAG_VARARG)) { option.insert_text += "("; } else { @@ -1054,10 +1065,8 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool } static const char *_keywords[] = { - "and", "in", "not", "or", "false", "PI", "TAU", "INF", "NAN", "self", "true", "as", "assert", - "breakpoint", "class", "extends", "is", "func", "preload", "signal", "tool", "await", - "const", "enum", "static", "super", "var", "break", "continue", "if", "elif", - "else", "for", "pass", "return", "match", "while", + "false", "PI", "TAU", "INF", "NAN", "self", "true", "breakpoint", "tool", "super", + "break", "continue", "pass", "return", 0 }; @@ -1068,6 +1077,33 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool kw++; } + static const char *_keywords_with_space[] = { + "and", "in", "not", "or", "as", "class", "extends", "is", "func", "signal", "await", + "const", "enum", "static", "var", "if", "elif", "else", "for", "match", "while", + 0 + }; + + const char **kws = _keywords_with_space; + while (*kws) { + ScriptCodeCompletionOption option(*kws, ScriptCodeCompletionOption::KIND_PLAIN_TEXT); + option.insert_text += " "; + r_result.insert(option.display, option); + kws++; + } + + static const char *_keywords_with_args[] = { + "assert", "preload", + 0 + }; + + const char **kwa = _keywords_with_args; + while (*kwa) { + ScriptCodeCompletionOption option(*kwa, ScriptCodeCompletionOption::KIND_FUNCTION); + option.insert_text += "("; + r_result.insert(option.display, option); + kwa++; + } + Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); for (const Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E != nullptr; E = E->next()) { if (!E->value().is_singleton) { @@ -1259,8 +1295,8 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, r_type.type.builtin_type = GDScriptParser::get_builtin_type(call->function_name); found = true; break; - } else if (GDScriptParser::get_builtin_function(call->function_name) < GDScriptFunctions::FUNC_MAX) { - MethodInfo mi = GDScriptFunctions::get_info(GDScriptParser::get_builtin_function(call->function_name)); + } else if (GDScriptUtilityFunctions::function_exists(call->function_name)) { + MethodInfo mi = GDScriptUtilityFunctions::get_function_info(call->function_name); r_type = _type_from_property(mi.return_val); found = true; break; @@ -2313,8 +2349,8 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c GDScriptCompletionIdentifier connect_base; - if (GDScriptParser::get_builtin_function(call->function_name) < GDScriptFunctions::FUNC_MAX) { - MethodInfo info = GDScriptFunctions::get_info(GDScriptParser::get_builtin_function(call->function_name)); + if (GDScriptUtilityFunctions::function_exists(call->function_name)) { + MethodInfo info = GDScriptUtilityFunctions::get_function_info(call->function_name); r_arghint = _make_arguments_hint(info, p_argidx); return; } else if (GDScriptParser::get_builtin_type(call->function_name) < Variant::VARIANT_MAX) { @@ -2475,7 +2511,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path break; } - if (!type.enumeration.empty()) { + if (!type.enumeration.is_empty()) { _find_enumeration_candidates(completion_context, type.enumeration, options); r_forced = options.size() > 0; } else { @@ -2942,13 +2978,11 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol } } - for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) { - if (GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i)) == p_symbol) { - r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD; - r_result.class_name = "@GDScript"; - r_result.class_member = p_symbol; - return OK; - } + if (GDScriptUtilityFunctions::function_exists(p_symbol)) { + r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD; + r_result.class_name = "@GDScript"; + r_result.class_member = p_symbol; + return OK; } if ("PI" == p_symbol || "TAU" == p_symbol || "INF" == p_symbol || "NAN" == p_symbol) { diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index 32372439c5..2171426e6f 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -114,7 +114,7 @@ void GDScriptFunction::debug_get_stack_member_state(int p_line, List<Pair<String ERR_CONTINUE(!sdmap.has(sd.identifier)); sdmap[sd.identifier].pos.pop_back(); - if (sdmap[sd.identifier].pos.empty()) { + if (sdmap[sd.identifier].pos.is_empty()) { sdmap.erase(sd.identifier); } } diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index bb5cc1284d..6c791836b9 100644 --- a/modules/gdscript/gdscript_function.h +++ b/modules/gdscript/gdscript_function.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -38,6 +38,7 @@ #include "core/templates/pair.h" #include "core/templates/self_list.h" #include "core/variant/variant.h" +#include "gdscript_utility_functions.h" class GDScriptInstance; class GDScript; @@ -190,7 +191,9 @@ public: OPCODE_CALL, OPCODE_CALL_RETURN, OPCODE_CALL_ASYNC, - OPCODE_CALL_BUILT_IN, + OPCODE_CALL_UTILITY, + OPCODE_CALL_UTILITY_VALIDATED, + OPCODE_CALL_GDSCRIPT_UTILITY, OPCODE_CALL_BUILTIN_TYPE_VALIDATED, OPCODE_CALL_SELF_BASE, OPCODE_CALL_METHOD_BIND, @@ -344,6 +347,10 @@ private: const Variant::ValidatedBuiltInMethod *_builtin_methods_ptr = nullptr; int _constructors_count = 0; const Variant::ValidatedConstructor *_constructors_ptr = nullptr; + int _utilities_count = 0; + const Variant::ValidatedUtilityFunction *_utilities_ptr = nullptr; + int _gds_utilities_count = 0; + const GDScriptUtilityFunctions::FunctionPtr *_gds_utilities_ptr = nullptr; int _methods_count = 0; MethodBind **_methods_ptr = nullptr; const int *_code_ptr = nullptr; @@ -372,6 +379,8 @@ private: Vector<Variant::ValidatedIndexedGetter> indexed_getters; Vector<Variant::ValidatedBuiltInMethod> builtin_methods; Vector<Variant::ValidatedConstructor> constructors; + Vector<Variant::ValidatedUtilityFunction> utilities; + Vector<GDScriptUtilityFunctions::FunctionPtr> gds_utilities; Vector<MethodBind *> methods; Vector<int> code; Vector<GDScriptDataType> argument_types; @@ -379,6 +388,7 @@ private: #ifdef TOOLS_ENABLED Vector<StringName> arg_names; + Vector<Variant> default_arg_values; #endif List<StackDebug> stack_debug; @@ -458,6 +468,11 @@ public: ERR_FAIL_INDEX_V(p_idx, default_arguments.size(), Variant()); return default_arguments[p_idx]; } +#ifdef TOOLS_ENABLED + const Vector<Variant> &get_default_arg_values() const { + return default_arg_values; + } +#endif // TOOLS_ENABLED Variant call(GDScriptInstance *p_instance, const Variant **p_args, int p_argcount, Callable::CallError &r_err, CallState *p_state = nullptr); diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp deleted file mode 100644 index 3a7c1a8676..0000000000 --- a/modules/gdscript/gdscript_functions.cpp +++ /dev/null @@ -1,1942 +0,0 @@ -/*************************************************************************/ -/* gdscript_functions.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 "gdscript_functions.h" - -#include "core/io/json.h" -#include "core/io/marshalls.h" -#include "core/math/math_funcs.h" -#include "core/object/class_db.h" -#include "core/object/reference.h" -#include "core/os/os.h" -#include "core/variant/variant_parser.h" -#include "gdscript.h" - -const char *GDScriptFunctions::get_func_name(Function p_func) { - ERR_FAIL_INDEX_V(p_func, FUNC_MAX, ""); - - static const char *_names[FUNC_MAX] = { - "sin", - "cos", - "tan", - "sinh", - "cosh", - "tanh", - "asin", - "acos", - "atan", - "atan2", - "sqrt", - "fmod", - "fposmod", - "posmod", - "floor", - "ceil", - "round", - "abs", - "sign", - "pow", - "log", - "exp", - "is_nan", - "is_inf", - "is_equal_approx", - "is_zero_approx", - "ease", - "step_decimals", - "stepify", - "lerp", - "lerp_angle", - "inverse_lerp", - "range_lerp", - "smoothstep", - "move_toward", - "dectime", - "randomize", - "randi", - "randf", - "randf_range", - "randi_range", - "seed", - "rand_seed", - "deg2rad", - "rad2deg", - "linear2db", - "db2linear", - "polar2cartesian", - "cartesian2polar", - "wrapi", - "wrapf", - "max", - "min", - "clamp", - "nearest_po2", - "weakref", - "convert", - "typeof", - "type_exists", - "char", - "ord", - "str", - "print", - "printt", - "prints", - "printerr", - "printraw", - "print_debug", - "push_error", - "push_warning", - "var2str", - "str2var", - "var2bytes", - "bytes2var", - "range", - "load", - "inst2dict", - "dict2inst", - "validate_json", - "parse_json", - "to_json", - "hash", - "Color8", - "ColorN", - "print_stack", - "get_stack", - "instance_from_id", - "len", - "is_instance_valid", - }; - - return _names[p_func]; -} - -void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_count, Variant &r_ret, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; -#ifdef DEBUG_ENABLED - -#define VALIDATE_ARG_COUNT(m_count) \ - if (p_arg_count < m_count) { \ - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; \ - r_error.argument = m_count; \ - r_error.expected = m_count; \ - r_ret = Variant(); \ - return; \ - } \ - if (p_arg_count > m_count) { \ - r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; \ - r_error.argument = m_count; \ - r_error.expected = m_count; \ - r_ret = Variant(); \ - return; \ - } - -#define VALIDATE_ARG_NUM(m_arg) \ - if (!p_args[m_arg]->is_num()) { \ - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \ - r_error.argument = m_arg; \ - r_error.expected = Variant::FLOAT; \ - r_ret = Variant(); \ - return; \ - } - -#else - -#define VALIDATE_ARG_COUNT(m_count) -#define VALIDATE_ARG_NUM(m_arg) -#endif - - //using a switch, so the compiler generates a jumptable - - switch (p_func) { - case MATH_SIN: { - VALIDATE_ARG_COUNT(1); - VALIDATE_ARG_NUM(0); - r_ret = Math::sin((double)*p_args[0]); - } break; - case MATH_COS: { - VALIDATE_ARG_COUNT(1); - VALIDATE_ARG_NUM(0); - r_ret = Math::cos((double)*p_args[0]); - } break; - case MATH_TAN: { - VALIDATE_ARG_COUNT(1); - VALIDATE_ARG_NUM(0); - r_ret = Math::tan((double)*p_args[0]); - } break; - case MATH_SINH: { - VALIDATE_ARG_COUNT(1); - VALIDATE_ARG_NUM(0); - r_ret = Math::sinh((double)*p_args[0]); - } break; - case MATH_COSH: { - VALIDATE_ARG_COUNT(1); - VALIDATE_ARG_NUM(0); - r_ret = Math::cosh((double)*p_args[0]); - } break; - case MATH_TANH: { - VALIDATE_ARG_COUNT(1); - VALIDATE_ARG_NUM(0); - r_ret = Math::tanh((double)*p_args[0]); - } break; - case MATH_ASIN: { - VALIDATE_ARG_COUNT(1); - VALIDATE_ARG_NUM(0); - r_ret = Math::asin((double)*p_args[0]); - } break; - case MATH_ACOS: { - VALIDATE_ARG_COUNT(1); - VALIDATE_ARG_NUM(0); - r_ret = Math::acos((double)*p_args[0]); - } break; - case MATH_ATAN: { - VALIDATE_ARG_COUNT(1); - VALIDATE_ARG_NUM(0); - r_ret = Math::atan((double)*p_args[0]); - } break; - case MATH_ATAN2: { - VALIDATE_ARG_COUNT(2); - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - r_ret = Math::atan2((double)*p_args[0], (double)*p_args[1]); - } break; - case MATH_SQRT: { - VALIDATE_ARG_COUNT(1); - VALIDATE_ARG_NUM(0); - r_ret = Math::sqrt((double)*p_args[0]); - } break; - case MATH_FMOD: { - VALIDATE_ARG_COUNT(2); - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - r_ret = Math::fmod((double)*p_args[0], (double)*p_args[1]); - } break; - case MATH_FPOSMOD: { - VALIDATE_ARG_COUNT(2); - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - r_ret = Math::fposmod((double)*p_args[0], (double)*p_args[1]); - } break; - case MATH_POSMOD: { - VALIDATE_ARG_COUNT(2); - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - r_ret = Math::posmod((int)*p_args[0], (int)*p_args[1]); - } break; - case MATH_FLOOR: { - VALIDATE_ARG_COUNT(1); - VALIDATE_ARG_NUM(0); - r_ret = Math::floor((double)*p_args[0]); - } break; - case MATH_CEIL: { - VALIDATE_ARG_COUNT(1); - VALIDATE_ARG_NUM(0); - r_ret = Math::ceil((double)*p_args[0]); - } break; - case MATH_ROUND: { - VALIDATE_ARG_COUNT(1); - VALIDATE_ARG_NUM(0); - r_ret = Math::round((double)*p_args[0]); - } break; - case MATH_ABS: { - VALIDATE_ARG_COUNT(1); - if (p_args[0]->get_type() == Variant::INT) { - int64_t i = *p_args[0]; - r_ret = ABS(i); - } else if (p_args[0]->get_type() == Variant::FLOAT) { - double r = *p_args[0]; - r_ret = Math::abs(r); - } else { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::FLOAT; - r_ret = Variant(); - } - } break; - case MATH_SIGN: { - VALIDATE_ARG_COUNT(1); - if (p_args[0]->get_type() == Variant::INT) { - int64_t i = *p_args[0]; - r_ret = i < 0 ? -1 : (i > 0 ? +1 : 0); - } else if (p_args[0]->get_type() == Variant::FLOAT) { - double r = *p_args[0]; - r_ret = r < 0.0 ? -1.0 : (r > 0.0 ? +1.0 : 0.0); - } else { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::FLOAT; - r_ret = Variant(); - } - } break; - case MATH_POW: { - VALIDATE_ARG_COUNT(2); - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - r_ret = Math::pow((double)*p_args[0], (double)*p_args[1]); - } break; - case MATH_LOG: { - VALIDATE_ARG_COUNT(1); - VALIDATE_ARG_NUM(0); - r_ret = Math::log((double)*p_args[0]); - } break; - case MATH_EXP: { - VALIDATE_ARG_COUNT(1); - VALIDATE_ARG_NUM(0); - r_ret = Math::exp((double)*p_args[0]); - } break; - case MATH_ISNAN: { - VALIDATE_ARG_COUNT(1); - VALIDATE_ARG_NUM(0); - r_ret = Math::is_nan((double)*p_args[0]); - } break; - case MATH_ISINF: { - VALIDATE_ARG_COUNT(1); - VALIDATE_ARG_NUM(0); - r_ret = Math::is_inf((double)*p_args[0]); - } break; - case MATH_ISEQUALAPPROX: { - VALIDATE_ARG_COUNT(2); - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - r_ret = Math::is_equal_approx((real_t)*p_args[0], (real_t)*p_args[1]); - } break; - case MATH_ISZEROAPPROX: { - VALIDATE_ARG_COUNT(1); - VALIDATE_ARG_NUM(0); - r_ret = Math::is_zero_approx((real_t)*p_args[0]); - } break; - case MATH_EASE: { - VALIDATE_ARG_COUNT(2); - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - r_ret = Math::ease((double)*p_args[0], (double)*p_args[1]); - } break; - case MATH_STEP_DECIMALS: { - VALIDATE_ARG_COUNT(1); - VALIDATE_ARG_NUM(0); - r_ret = Math::step_decimals((double)*p_args[0]); - } break; - case MATH_STEPIFY: { - VALIDATE_ARG_COUNT(2); - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - r_ret = Math::stepify((double)*p_args[0], (double)*p_args[1]); - } break; - case MATH_LERP: { - VALIDATE_ARG_COUNT(3); - VALIDATE_ARG_NUM(2); - const double t = (double)*p_args[2]; - switch (p_args[0]->get_type() == p_args[1]->get_type() ? p_args[0]->get_type() : Variant::FLOAT) { - case Variant::VECTOR2: { - r_ret = ((Vector2)*p_args[0]).lerp((Vector2)*p_args[1], t); - } break; - case Variant::VECTOR3: { - r_ret = (p_args[0]->operator Vector3()).lerp(p_args[1]->operator Vector3(), t); - } break; - case Variant::COLOR: { - r_ret = ((Color)*p_args[0]).lerp((Color)*p_args[1], t); - } break; - default: { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - r_ret = Math::lerp((double)*p_args[0], (double)*p_args[1], t); - } break; - } - } break; - case MATH_LERP_ANGLE: { - VALIDATE_ARG_COUNT(3); - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - VALIDATE_ARG_NUM(2); - r_ret = Math::lerp_angle((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]); - } break; - case MATH_INVERSE_LERP: { - VALIDATE_ARG_COUNT(3); - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - VALIDATE_ARG_NUM(2); - r_ret = Math::inverse_lerp((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]); - } break; - case MATH_RANGE_LERP: { - VALIDATE_ARG_COUNT(5); - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - VALIDATE_ARG_NUM(2); - VALIDATE_ARG_NUM(3); - VALIDATE_ARG_NUM(4); - r_ret = Math::range_lerp((double)*p_args[0], (double)*p_args[1], (double)*p_args[2], (double)*p_args[3], (double)*p_args[4]); - } break; - case MATH_SMOOTHSTEP: { - VALIDATE_ARG_COUNT(3); - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - VALIDATE_ARG_NUM(2); - r_ret = Math::smoothstep((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]); - } break; - case MATH_MOVE_TOWARD: { - VALIDATE_ARG_COUNT(3); - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - VALIDATE_ARG_NUM(2); - r_ret = Math::move_toward((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]); - } break; - case MATH_DECTIME: { - VALIDATE_ARG_COUNT(3); - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - VALIDATE_ARG_NUM(2); - r_ret = Math::dectime((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]); - } break; - case MATH_RANDOMIZE: { - VALIDATE_ARG_COUNT(0); - Math::randomize(); - r_ret = Variant(); - } break; - case MATH_RANDI: { - VALIDATE_ARG_COUNT(0); - r_ret = Math::rand(); - } break; - case MATH_RANDF: { - VALIDATE_ARG_COUNT(0); - r_ret = Math::randf(); - } break; - case MATH_RANDF_RANGE: { - VALIDATE_ARG_COUNT(2); - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - r_ret = Math::random((double)*p_args[0], (double)*p_args[1]); - } break; - case MATH_RANDI_RANGE: { - VALIDATE_ARG_COUNT(2); - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - r_ret = Math::random((int)*p_args[0], (int)*p_args[1]); - } break; - case MATH_SEED: { - VALIDATE_ARG_COUNT(1); - VALIDATE_ARG_NUM(0); - uint64_t seed = *p_args[0]; - Math::seed(seed); - r_ret = Variant(); - } break; - case MATH_RANDSEED: { - VALIDATE_ARG_COUNT(1); - VALIDATE_ARG_NUM(0); - uint64_t seed = *p_args[0]; - int ret = Math::rand_from_seed(&seed); - Array reta; - reta.push_back(ret); - reta.push_back(seed); - r_ret = reta; - - } break; - case MATH_DEG2RAD: { - VALIDATE_ARG_COUNT(1); - VALIDATE_ARG_NUM(0); - r_ret = Math::deg2rad((double)*p_args[0]); - } break; - case MATH_RAD2DEG: { - VALIDATE_ARG_COUNT(1); - VALIDATE_ARG_NUM(0); - r_ret = Math::rad2deg((double)*p_args[0]); - } break; - case MATH_LINEAR2DB: { - VALIDATE_ARG_COUNT(1); - VALIDATE_ARG_NUM(0); - r_ret = Math::linear2db((double)*p_args[0]); - } break; - case MATH_DB2LINEAR: { - VALIDATE_ARG_COUNT(1); - VALIDATE_ARG_NUM(0); - r_ret = Math::db2linear((double)*p_args[0]); - } break; - case MATH_POLAR2CARTESIAN: { - VALIDATE_ARG_COUNT(2); - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - double r = *p_args[0]; - double th = *p_args[1]; - r_ret = Vector2(r * Math::cos(th), r * Math::sin(th)); - } break; - case MATH_CARTESIAN2POLAR: { - VALIDATE_ARG_COUNT(2); - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - double x = *p_args[0]; - double y = *p_args[1]; - r_ret = Vector2(Math::sqrt(x * x + y * y), Math::atan2(y, x)); - } break; - case MATH_WRAP: { - VALIDATE_ARG_COUNT(3); - r_ret = Math::wrapi((int64_t)*p_args[0], (int64_t)*p_args[1], (int64_t)*p_args[2]); - } break; - case MATH_WRAPF: { - VALIDATE_ARG_COUNT(3); - r_ret = Math::wrapf((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]); - } break; - case LOGIC_MAX: { - VALIDATE_ARG_COUNT(2); - if (p_args[0]->get_type() == Variant::INT && p_args[1]->get_type() == Variant::INT) { - int64_t a = *p_args[0]; - int64_t b = *p_args[1]; - r_ret = MAX(a, b); - } else { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - - double a = *p_args[0]; - double b = *p_args[1]; - - r_ret = MAX(a, b); - } - - } break; - case LOGIC_MIN: { - VALIDATE_ARG_COUNT(2); - if (p_args[0]->get_type() == Variant::INT && p_args[1]->get_type() == Variant::INT) { - int64_t a = *p_args[0]; - int64_t b = *p_args[1]; - r_ret = MIN(a, b); - } else { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - - double a = *p_args[0]; - double b = *p_args[1]; - - r_ret = MIN(a, b); - } - } break; - case LOGIC_CLAMP: { - VALIDATE_ARG_COUNT(3); - if (p_args[0]->get_type() == Variant::INT && p_args[1]->get_type() == Variant::INT && p_args[2]->get_type() == Variant::INT) { - int64_t a = *p_args[0]; - int64_t b = *p_args[1]; - int64_t c = *p_args[2]; - r_ret = CLAMP(a, b, c); - } else { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - VALIDATE_ARG_NUM(2); - - double a = *p_args[0]; - double b = *p_args[1]; - double c = *p_args[2]; - - r_ret = CLAMP(a, b, c); - } - } break; - case LOGIC_NEAREST_PO2: { - VALIDATE_ARG_COUNT(1); - VALIDATE_ARG_NUM(0); - int64_t num = *p_args[0]; - r_ret = next_power_of_2(num); - } break; - case OBJ_WEAKREF: { - VALIDATE_ARG_COUNT(1); - if (p_args[0]->get_type() == Variant::OBJECT) { - if (p_args[0]->is_ref()) { - Ref<WeakRef> wref = memnew(WeakRef); - REF r = *p_args[0]; - if (r.is_valid()) { - wref->set_ref(r); - } - r_ret = wref; - } else { - Ref<WeakRef> wref = memnew(WeakRef); - Object *obj = *p_args[0]; - if (obj) { - wref->set_obj(obj); - } - r_ret = wref; - } - } else if (p_args[0]->get_type() == Variant::NIL) { - Ref<WeakRef> wref = memnew(WeakRef); - r_ret = wref; - } else { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::OBJECT; - r_ret = Variant(); - return; - } - } break; - case TYPE_CONVERT: { - VALIDATE_ARG_COUNT(2); - VALIDATE_ARG_NUM(1); - int type = *p_args[1]; - if (type < 0 || type >= Variant::VARIANT_MAX) { - r_ret = RTR("Invalid type argument to convert(), use TYPE_* constants."); - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::INT; - return; - - } else { - Variant::construct(Variant::Type(type), r_ret, p_args, 1, r_error); - } - } break; - case TYPE_OF: { - VALIDATE_ARG_COUNT(1); - r_ret = p_args[0]->get_type(); - - } break; - case TYPE_EXISTS: { - VALIDATE_ARG_COUNT(1); - r_ret = ClassDB::class_exists(*p_args[0]); - - } break; - case TEXT_CHAR: { - VALIDATE_ARG_COUNT(1); - VALIDATE_ARG_NUM(0); - char32_t result[2] = { *p_args[0], 0 }; - r_ret = String(result); - } break; - case TEXT_ORD: { - VALIDATE_ARG_COUNT(1); - - if (p_args[0]->get_type() != Variant::STRING) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::STRING; - r_ret = Variant(); - return; - } - - String str = p_args[0]->operator String(); - - if (str.length() != 1) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::STRING; - r_ret = RTR("Expected a string of length 1 (a character)."); - return; - } - - r_ret = str.get(0); - - } break; - case TEXT_STR: { - if (p_arg_count < 1) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 1; - r_ret = Variant(); - - return; - } - String str; - for (int i = 0; i < p_arg_count; i++) { - String os = p_args[i]->operator String(); - - if (i == 0) { - str = os; - } else { - str += os; - } - } - - r_ret = str; - - } break; - case TEXT_PRINT: { - String str; - for (int i = 0; i < p_arg_count; i++) { - str += p_args[i]->operator String(); - } - - print_line(str); - r_ret = Variant(); - - } break; - case TEXT_PRINT_TABBED: { - String str; - for (int i = 0; i < p_arg_count; i++) { - if (i) { - str += "\t"; - } - str += p_args[i]->operator String(); - } - - print_line(str); - r_ret = Variant(); - - } break; - case TEXT_PRINT_SPACED: { - String str; - for (int i = 0; i < p_arg_count; i++) { - if (i) { - str += " "; - } - str += p_args[i]->operator String(); - } - - print_line(str); - r_ret = Variant(); - - } break; - - case TEXT_PRINTERR: { - String str; - for (int i = 0; i < p_arg_count; i++) { - str += p_args[i]->operator String(); - } - - print_error(str); - r_ret = Variant(); - - } break; - case TEXT_PRINTRAW: { - String str; - for (int i = 0; i < p_arg_count; i++) { - str += p_args[i]->operator String(); - } - - OS::get_singleton()->print("%s", str.utf8().get_data()); - r_ret = Variant(); - - } break; - case TEXT_PRINT_DEBUG: { - String str; - for (int i = 0; i < p_arg_count; i++) { - str += p_args[i]->operator String(); - } - - ScriptLanguage *script = GDScriptLanguage::get_singleton(); - if (script->debug_get_stack_level_count() > 0) { - str += "\n At: " + script->debug_get_stack_level_source(0) + ":" + itos(script->debug_get_stack_level_line(0)) + ":" + script->debug_get_stack_level_function(0) + "()"; - } - - print_line(str); - r_ret = Variant(); - } break; - case PUSH_ERROR: { - VALIDATE_ARG_COUNT(1); - if (p_args[0]->get_type() != Variant::STRING) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::STRING; - r_ret = Variant(); - break; - } - - String message = *p_args[0]; - ERR_PRINT(message); - r_ret = Variant(); - } break; - case PUSH_WARNING: { - VALIDATE_ARG_COUNT(1); - if (p_args[0]->get_type() != Variant::STRING) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::STRING; - r_ret = Variant(); - break; - } - - String message = *p_args[0]; - WARN_PRINT(message); - r_ret = Variant(); - } break; - case VAR_TO_STR: { - VALIDATE_ARG_COUNT(1); - String vars; - VariantWriter::write_to_string(*p_args[0], vars); - r_ret = vars; - } break; - case STR_TO_VAR: { - VALIDATE_ARG_COUNT(1); - if (p_args[0]->get_type() != Variant::STRING) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::STRING; - r_ret = Variant(); - return; - } - r_ret = *p_args[0]; - - VariantParser::StreamString ss; - ss.s = *p_args[0]; - - String errs; - int line; - (void)VariantParser::parse(&ss, r_ret, errs, line); - } break; - case VAR_TO_BYTES: { - bool full_objects = false; - if (p_arg_count < 1) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 1; - r_ret = Variant(); - return; - } else if (p_arg_count > 2) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; - r_error.argument = 2; - r_ret = Variant(); - } else if (p_arg_count == 2) { - if (p_args[1]->get_type() != Variant::BOOL) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 1; - r_error.expected = Variant::BOOL; - r_ret = Variant(); - return; - } - full_objects = *p_args[1]; - } - - PackedByteArray barr; - int len; - Error err = encode_variant(*p_args[0], nullptr, len, full_objects); - if (err) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::NIL; - r_ret = "Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID)."; - return; - } - - barr.resize(len); - { - uint8_t *w = barr.ptrw(); - encode_variant(*p_args[0], w, len, full_objects); - } - r_ret = barr; - } break; - case BYTES_TO_VAR: { - bool allow_objects = false; - if (p_arg_count < 1) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 1; - r_ret = Variant(); - return; - } else if (p_arg_count > 2) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; - r_error.argument = 2; - r_ret = Variant(); - } else if (p_arg_count == 2) { - if (p_args[1]->get_type() != Variant::BOOL) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 1; - r_error.expected = Variant::BOOL; - r_ret = Variant(); - return; - } - allow_objects = *p_args[1]; - } - - if (p_args[0]->get_type() != Variant::PACKED_BYTE_ARRAY) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 1; - r_error.expected = Variant::PACKED_BYTE_ARRAY; - r_ret = Variant(); - return; - } - - PackedByteArray varr = *p_args[0]; - Variant ret; - { - const uint8_t *r = varr.ptr(); - Error err = decode_variant(ret, r, varr.size(), nullptr, allow_objects); - if (err != OK) { - r_ret = RTR("Not enough bytes for decoding bytes, or invalid format."); - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::PACKED_BYTE_ARRAY; - return; - } - } - - r_ret = ret; - - } break; - case GEN_RANGE: { - switch (p_arg_count) { - case 0: { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 1; - r_error.expected = 1; - r_ret = Variant(); - - } break; - case 1: { - VALIDATE_ARG_NUM(0); - int count = *p_args[0]; - Array arr; - if (count <= 0) { - r_ret = arr; - return; - } - Error err = arr.resize(count); - if (err != OK) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - r_ret = Variant(); - return; - } - - for (int i = 0; i < count; i++) { - arr[i] = i; - } - - r_ret = arr; - } break; - case 2: { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - - int from = *p_args[0]; - int to = *p_args[1]; - - Array arr; - if (from >= to) { - r_ret = arr; - return; - } - Error err = arr.resize(to - from); - if (err != OK) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - r_ret = Variant(); - return; - } - for (int i = from; i < to; i++) { - arr[i - from] = i; - } - r_ret = arr; - } break; - case 3: { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - VALIDATE_ARG_NUM(2); - - int from = *p_args[0]; - int to = *p_args[1]; - int incr = *p_args[2]; - if (incr == 0) { - r_ret = RTR("Step argument is zero!"); - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return; - } - - Array arr; - if (from >= to && incr > 0) { - r_ret = arr; - return; - } - if (from <= to && incr < 0) { - r_ret = arr; - return; - } - - //calculate how many - int count = 0; - if (incr > 0) { - count = ((to - from - 1) / incr) + 1; - } else { - count = ((from - to - 1) / -incr) + 1; - } - - Error err = arr.resize(count); - - if (err != OK) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - r_ret = Variant(); - return; - } - - if (incr > 0) { - int idx = 0; - for (int i = from; i < to; i += incr) { - arr[idx++] = i; - } - } else { - int idx = 0; - for (int i = from; i > to; i += incr) { - arr[idx++] = i; - } - } - - r_ret = arr; - } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; - r_error.argument = 3; - r_error.expected = 3; - r_ret = Variant(); - - } break; - } - - } break; - case RESOURCE_LOAD: { - VALIDATE_ARG_COUNT(1); - if (p_args[0]->get_type() != Variant::STRING) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::STRING; - r_ret = Variant(); - } else { - r_ret = ResourceLoader::load(*p_args[0]); - } - - } break; - case INST2DICT: { - VALIDATE_ARG_COUNT(1); - - if (p_args[0]->get_type() == Variant::NIL) { - r_ret = Variant(); - } else if (p_args[0]->get_type() != Variant::OBJECT) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_ret = Variant(); - } else { - Object *obj = *p_args[0]; - if (!obj) { - r_ret = Variant(); - - } else if (!obj->get_script_instance() || obj->get_script_instance()->get_language() != GDScriptLanguage::get_singleton()) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::DICTIONARY; - r_ret = RTR("Not a script with an instance"); - return; - } else { - GDScriptInstance *ins = static_cast<GDScriptInstance *>(obj->get_script_instance()); - Ref<GDScript> base = ins->get_script(); - if (base.is_null()) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::DICTIONARY; - r_ret = RTR("Not based on a script"); - return; - } - - GDScript *p = base.ptr(); - Vector<StringName> sname; - - while (p->_owner) { - sname.push_back(p->name); - p = p->_owner; - } - sname.invert(); - - if (!p->path.is_resource_file()) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::DICTIONARY; - r_ret = Variant(); - - r_ret = RTR("Not based on a resource file"); - - return; - } - - NodePath cp(sname, Vector<StringName>(), false); - - Dictionary d; - d["@subpath"] = cp; - d["@path"] = p->get_path(); - - for (Map<StringName, GDScript::MemberInfo>::Element *E = base->member_indices.front(); E; E = E->next()) { - if (!d.has(E->key())) { - d[E->key()] = ins->members[E->get().index]; - } - } - r_ret = d; - } - } - - } break; - case DICT2INST: { - VALIDATE_ARG_COUNT(1); - - if (p_args[0]->get_type() != Variant::DICTIONARY) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::DICTIONARY; - r_ret = Variant(); - - return; - } - - Dictionary d = *p_args[0]; - - if (!d.has("@path")) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::OBJECT; - r_ret = RTR("Invalid instance dictionary format (missing @path)"); - - return; - } - - Ref<Script> scr = ResourceLoader::load(d["@path"]); - if (!scr.is_valid()) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::OBJECT; - r_ret = RTR("Invalid instance dictionary format (can't load script at @path)"); - return; - } - - Ref<GDScript> gdscr = scr; - - if (!gdscr.is_valid()) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::OBJECT; - r_ret = Variant(); - r_ret = RTR("Invalid instance dictionary format (invalid script at @path)"); - return; - } - - NodePath sub; - if (d.has("@subpath")) { - sub = d["@subpath"]; - } - - for (int i = 0; i < sub.get_name_count(); i++) { - gdscr = gdscr->subclasses[sub.get_name(i)]; - if (!gdscr.is_valid()) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::OBJECT; - r_ret = Variant(); - r_ret = RTR("Invalid instance dictionary (invalid subclasses)"); - return; - } - } - r_ret = gdscr->_new(nullptr, -1 /*skip initializer*/, r_error); - - if (r_error.error != Callable::CallError::CALL_OK) { - r_ret = Variant(); - return; - } - - GDScriptInstance *ins = static_cast<GDScriptInstance *>(static_cast<Object *>(r_ret)->get_script_instance()); - Ref<GDScript> gd_ref = ins->get_script(); - - for (Map<StringName, GDScript::MemberInfo>::Element *E = gd_ref->member_indices.front(); E; E = E->next()) { - if (d.has(E->key())) { - ins->members.write[E->get().index] = d[E->key()]; - } - } - - } break; - case VALIDATE_JSON: { - VALIDATE_ARG_COUNT(1); - - if (p_args[0]->get_type() != Variant::STRING) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::STRING; - r_ret = Variant(); - return; - } - - String errs; - int errl; - - Error err = JSON::parse(*p_args[0], r_ret, errs, errl); - - if (err != OK) { - r_ret = itos(errl) + ":" + errs; - } else { - r_ret = ""; - } - - } break; - case PARSE_JSON: { - VALIDATE_ARG_COUNT(1); - - if (p_args[0]->get_type() != Variant::STRING) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::STRING; - r_ret = Variant(); - return; - } - - String errs; - int errl; - - Error err = JSON::parse(*p_args[0], r_ret, errs, errl); - - if (err != OK) { - r_ret = Variant(); - ERR_PRINT(vformat("Error parsing JSON at line %s: %s", errl, errs)); - } - - } break; - case TO_JSON: { - VALIDATE_ARG_COUNT(1); - - r_ret = JSON::print(*p_args[0]); - } break; - case HASH: { - VALIDATE_ARG_COUNT(1); - r_ret = p_args[0]->hash(); - - } break; - case COLOR8: { - if (p_arg_count < 3) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 3; - r_ret = Variant(); - - return; - } - if (p_arg_count > 4) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; - r_error.argument = 4; - r_ret = Variant(); - - return; - } - - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - VALIDATE_ARG_NUM(2); - - Color color((float)*p_args[0] / 255.0f, (float)*p_args[1] / 255.0f, (float)*p_args[2] / 255.0f); - - if (p_arg_count == 4) { - VALIDATE_ARG_NUM(3); - color.a = (float)*p_args[3] / 255.0f; - } - - r_ret = color; - - } break; - case COLORN: { - if (p_arg_count < 1) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 1; - r_ret = Variant(); - return; - } - - if (p_arg_count > 2) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; - r_error.argument = 2; - r_ret = Variant(); - return; - } - - if (p_args[0]->get_type() != Variant::STRING) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_ret = Variant(); - } else { - Color color = Color::named(*p_args[0]); - if (p_arg_count == 2) { - VALIDATE_ARG_NUM(1); - color.a = *p_args[1]; - } - r_ret = color; - } - - } break; - - case PRINT_STACK: { - VALIDATE_ARG_COUNT(0); - - ScriptLanguage *script = GDScriptLanguage::get_singleton(); - for (int i = 0; i < script->debug_get_stack_level_count(); i++) { - print_line("Frame " + itos(i) + " - " + script->debug_get_stack_level_source(i) + ":" + itos(script->debug_get_stack_level_line(i)) + " in function '" + script->debug_get_stack_level_function(i) + "'"); - }; - } break; - - case GET_STACK: { - VALIDATE_ARG_COUNT(0); - - ScriptLanguage *script = GDScriptLanguage::get_singleton(); - Array ret; - for (int i = 0; i < script->debug_get_stack_level_count(); i++) { - Dictionary frame; - frame["source"] = script->debug_get_stack_level_source(i); - frame["function"] = script->debug_get_stack_level_function(i); - frame["line"] = script->debug_get_stack_level_line(i); - ret.push_back(frame); - }; - r_ret = ret; - } break; - - case INSTANCE_FROM_ID: { - VALIDATE_ARG_COUNT(1); - if (p_args[0]->get_type() != Variant::INT && p_args[0]->get_type() != Variant::FLOAT) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::INT; - r_ret = Variant(); - break; - } - - ObjectID id = *p_args[0]; - r_ret = ObjectDB::get_instance(id); - - } break; - case LEN: { - VALIDATE_ARG_COUNT(1); - switch (p_args[0]->get_type()) { - case Variant::STRING: { - String d = *p_args[0]; - r_ret = d.length(); - } break; - case Variant::DICTIONARY: { - Dictionary d = *p_args[0]; - r_ret = d.size(); - } break; - case Variant::ARRAY: { - Array d = *p_args[0]; - r_ret = d.size(); - } break; - case Variant::PACKED_BYTE_ARRAY: { - Vector<uint8_t> d = *p_args[0]; - r_ret = d.size(); - } break; - case Variant::PACKED_INT32_ARRAY: { - Vector<int32_t> d = *p_args[0]; - r_ret = d.size(); - } break; - case Variant::PACKED_INT64_ARRAY: { - Vector<int64_t> d = *p_args[0]; - r_ret = d.size(); - } break; - case Variant::PACKED_FLOAT32_ARRAY: { - Vector<float> d = *p_args[0]; - r_ret = d.size(); - } break; - case Variant::PACKED_FLOAT64_ARRAY: { - Vector<double> d = *p_args[0]; - r_ret = d.size(); - } break; - case Variant::PACKED_STRING_ARRAY: { - Vector<String> d = *p_args[0]; - r_ret = d.size(); - } break; - case Variant::PACKED_VECTOR2_ARRAY: { - Vector<Vector2> d = *p_args[0]; - r_ret = d.size(); - } break; - case Variant::PACKED_VECTOR3_ARRAY: { - Vector<Vector3> d = *p_args[0]; - r_ret = d.size(); - } break; - case Variant::PACKED_COLOR_ARRAY: { - Vector<Color> d = *p_args[0]; - r_ret = d.size(); - } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::OBJECT; - r_ret = Variant(); - r_ret = RTR("Object can't provide a length."); - } - } - - } break; - case IS_INSTANCE_VALID: { - VALIDATE_ARG_COUNT(1); - if (p_args[0]->get_type() != Variant::OBJECT) { - r_ret = false; - } else { - Object *obj = p_args[0]->get_validated_object(); - r_ret = obj != nullptr; - } - - } break; - case FUNC_MAX: { - ERR_FAIL(); - } break; - } -} - -bool GDScriptFunctions::is_deterministic(Function p_func) { - //man i couldn't have chosen a worse function name, - //way too controversial.. - - switch (p_func) { - case MATH_SIN: - case MATH_COS: - case MATH_TAN: - case MATH_SINH: - case MATH_COSH: - case MATH_TANH: - case MATH_ASIN: - case MATH_ACOS: - case MATH_ATAN: - case MATH_ATAN2: - case MATH_SQRT: - case MATH_FMOD: - case MATH_FPOSMOD: - case MATH_POSMOD: - case MATH_FLOOR: - case MATH_CEIL: - case MATH_ROUND: - case MATH_ABS: - case MATH_SIGN: - case MATH_POW: - case MATH_LOG: - case MATH_EXP: - case MATH_ISNAN: - case MATH_ISINF: - case MATH_EASE: - case MATH_STEP_DECIMALS: - case MATH_STEPIFY: - case MATH_LERP: - case MATH_INVERSE_LERP: - case MATH_RANGE_LERP: - case MATH_SMOOTHSTEP: - case MATH_MOVE_TOWARD: - case MATH_DECTIME: - case MATH_DEG2RAD: - case MATH_RAD2DEG: - case MATH_LINEAR2DB: - case MATH_DB2LINEAR: - case MATH_POLAR2CARTESIAN: - case MATH_CARTESIAN2POLAR: - case MATH_WRAP: - case MATH_WRAPF: - case LOGIC_MAX: - case LOGIC_MIN: - case LOGIC_CLAMP: - case LOGIC_NEAREST_PO2: - case TYPE_CONVERT: - case TYPE_OF: - case TYPE_EXISTS: - case TEXT_CHAR: - case TEXT_ORD: - case TEXT_STR: - case COLOR8: - case LEN: - // enable for debug only, otherwise not desirable - case GEN_RANGE: - return true; - default: - return false; - } - - return false; -} - -MethodInfo GDScriptFunctions::get_info(Function p_func) { -#ifdef DEBUG_ENABLED - //using a switch, so the compiler generates a jumptable - - switch (p_func) { - case MATH_SIN: { - MethodInfo mi("sin", PropertyInfo(Variant::FLOAT, "s")); - mi.return_val.type = Variant::FLOAT; - return mi; - - } break; - case MATH_COS: { - MethodInfo mi("cos", PropertyInfo(Variant::FLOAT, "s")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_TAN: { - MethodInfo mi("tan", PropertyInfo(Variant::FLOAT, "s")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_SINH: { - MethodInfo mi("sinh", PropertyInfo(Variant::FLOAT, "s")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_COSH: { - MethodInfo mi("cosh", PropertyInfo(Variant::FLOAT, "s")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_TANH: { - MethodInfo mi("tanh", PropertyInfo(Variant::FLOAT, "s")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_ASIN: { - MethodInfo mi("asin", PropertyInfo(Variant::FLOAT, "s")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_ACOS: { - MethodInfo mi("acos", PropertyInfo(Variant::FLOAT, "s")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_ATAN: { - MethodInfo mi("atan", PropertyInfo(Variant::FLOAT, "s")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_ATAN2: { - MethodInfo mi("atan2", PropertyInfo(Variant::FLOAT, "y"), PropertyInfo(Variant::FLOAT, "x")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_SQRT: { - MethodInfo mi("sqrt", PropertyInfo(Variant::FLOAT, "s")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_FMOD: { - MethodInfo mi("fmod", PropertyInfo(Variant::FLOAT, "a"), PropertyInfo(Variant::FLOAT, "b")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_FPOSMOD: { - MethodInfo mi("fposmod", PropertyInfo(Variant::FLOAT, "a"), PropertyInfo(Variant::FLOAT, "b")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_POSMOD: { - MethodInfo mi("posmod", PropertyInfo(Variant::INT, "a"), PropertyInfo(Variant::INT, "b")); - mi.return_val.type = Variant::INT; - return mi; - } break; - case MATH_FLOOR: { - MethodInfo mi("floor", PropertyInfo(Variant::FLOAT, "s")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_CEIL: { - MethodInfo mi("ceil", PropertyInfo(Variant::FLOAT, "s")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_ROUND: { - MethodInfo mi("round", PropertyInfo(Variant::FLOAT, "s")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_ABS: { - MethodInfo mi("abs", PropertyInfo(Variant::FLOAT, "s")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_SIGN: { - MethodInfo mi("sign", PropertyInfo(Variant::FLOAT, "s")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_POW: { - MethodInfo mi("pow", PropertyInfo(Variant::FLOAT, "base"), PropertyInfo(Variant::FLOAT, "exp")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_LOG: { - MethodInfo mi("log", PropertyInfo(Variant::FLOAT, "s")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_EXP: { - MethodInfo mi("exp", PropertyInfo(Variant::FLOAT, "s")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_ISNAN: { - MethodInfo mi("is_nan", PropertyInfo(Variant::FLOAT, "s")); - mi.return_val.type = Variant::BOOL; - return mi; - } break; - case MATH_ISINF: { - MethodInfo mi("is_inf", PropertyInfo(Variant::FLOAT, "s")); - mi.return_val.type = Variant::BOOL; - return mi; - } break; - case MATH_ISEQUALAPPROX: { - MethodInfo mi("is_equal_approx", PropertyInfo(Variant::FLOAT, "a"), PropertyInfo(Variant::FLOAT, "b")); - mi.return_val.type = Variant::BOOL; - return mi; - } break; - case MATH_ISZEROAPPROX: { - MethodInfo mi("is_zero_approx", PropertyInfo(Variant::FLOAT, "s")); - mi.return_val.type = Variant::BOOL; - return mi; - } break; - case MATH_EASE: { - MethodInfo mi("ease", PropertyInfo(Variant::FLOAT, "s"), PropertyInfo(Variant::FLOAT, "curve")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_STEP_DECIMALS: { - MethodInfo mi("step_decimals", PropertyInfo(Variant::FLOAT, "step")); - mi.return_val.type = Variant::INT; - return mi; - } break; - case MATH_STEPIFY: { - MethodInfo mi("stepify", PropertyInfo(Variant::FLOAT, "s"), PropertyInfo(Variant::FLOAT, "step")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_LERP: { - MethodInfo mi("lerp", PropertyInfo(Variant::NIL, "from"), PropertyInfo(Variant::NIL, "to"), PropertyInfo(Variant::FLOAT, "weight")); - mi.return_val.type = Variant::NIL; - mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; - return mi; - } break; - case MATH_LERP_ANGLE: { - MethodInfo mi("lerp_angle", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to"), PropertyInfo(Variant::FLOAT, "weight")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_INVERSE_LERP: { - MethodInfo mi("inverse_lerp", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to"), PropertyInfo(Variant::FLOAT, "weight")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_RANGE_LERP: { - MethodInfo mi("range_lerp", PropertyInfo(Variant::FLOAT, "value"), PropertyInfo(Variant::FLOAT, "istart"), PropertyInfo(Variant::FLOAT, "istop"), PropertyInfo(Variant::FLOAT, "ostart"), PropertyInfo(Variant::FLOAT, "ostop")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_SMOOTHSTEP: { - MethodInfo mi("smoothstep", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to"), PropertyInfo(Variant::FLOAT, "s")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_MOVE_TOWARD: { - MethodInfo mi("move_toward", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to"), PropertyInfo(Variant::FLOAT, "delta")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_DECTIME: { - MethodInfo mi("dectime", PropertyInfo(Variant::FLOAT, "value"), PropertyInfo(Variant::FLOAT, "amount"), PropertyInfo(Variant::FLOAT, "step")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_RANDOMIZE: { - MethodInfo mi("randomize"); - mi.return_val.type = Variant::NIL; - return mi; - } break; - case MATH_RANDI: { - MethodInfo mi("randi"); - mi.return_val.type = Variant::INT; - return mi; - } break; - case MATH_RANDF: { - MethodInfo mi("randf"); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_RANDF_RANGE: { - MethodInfo mi("randf_range", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_RANDI_RANGE: { - MethodInfo mi("randi_range", PropertyInfo(Variant::INT, "from"), PropertyInfo(Variant::INT, "to")); - mi.return_val.type = Variant::INT; - return mi; - } break; - case MATH_SEED: { - MethodInfo mi("seed", PropertyInfo(Variant::INT, "seed")); - mi.return_val.type = Variant::NIL; - return mi; - } break; - case MATH_RANDSEED: { - MethodInfo mi("rand_seed", PropertyInfo(Variant::INT, "seed")); - mi.return_val.type = Variant::ARRAY; - return mi; - } break; - case MATH_DEG2RAD: { - MethodInfo mi("deg2rad", PropertyInfo(Variant::FLOAT, "deg")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_RAD2DEG: { - MethodInfo mi("rad2deg", PropertyInfo(Variant::FLOAT, "rad")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_LINEAR2DB: { - MethodInfo mi("linear2db", PropertyInfo(Variant::FLOAT, "nrg")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_DB2LINEAR: { - MethodInfo mi("db2linear", PropertyInfo(Variant::FLOAT, "db")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case MATH_POLAR2CARTESIAN: { - MethodInfo mi("polar2cartesian", PropertyInfo(Variant::FLOAT, "r"), PropertyInfo(Variant::FLOAT, "th")); - mi.return_val.type = Variant::VECTOR2; - return mi; - } break; - case MATH_CARTESIAN2POLAR: { - MethodInfo mi("cartesian2polar", PropertyInfo(Variant::FLOAT, "x"), PropertyInfo(Variant::FLOAT, "y")); - mi.return_val.type = Variant::VECTOR2; - return mi; - } break; - case MATH_WRAP: { - MethodInfo mi("wrapi", PropertyInfo(Variant::INT, "value"), PropertyInfo(Variant::INT, "min"), PropertyInfo(Variant::INT, "max")); - mi.return_val.type = Variant::INT; - return mi; - } break; - case MATH_WRAPF: { - MethodInfo mi("wrapf", PropertyInfo(Variant::FLOAT, "value"), PropertyInfo(Variant::FLOAT, "min"), PropertyInfo(Variant::FLOAT, "max")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case LOGIC_MAX: { - MethodInfo mi("max", PropertyInfo(Variant::FLOAT, "a"), PropertyInfo(Variant::FLOAT, "b")); - mi.return_val.type = Variant::FLOAT; - return mi; - - } break; - case LOGIC_MIN: { - MethodInfo mi("min", PropertyInfo(Variant::FLOAT, "a"), PropertyInfo(Variant::FLOAT, "b")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case LOGIC_CLAMP: { - MethodInfo mi("clamp", PropertyInfo(Variant::FLOAT, "value"), PropertyInfo(Variant::FLOAT, "min"), PropertyInfo(Variant::FLOAT, "max")); - mi.return_val.type = Variant::FLOAT; - return mi; - } break; - case LOGIC_NEAREST_PO2: { - MethodInfo mi("nearest_po2", PropertyInfo(Variant::INT, "value")); - mi.return_val.type = Variant::INT; - return mi; - } break; - case OBJ_WEAKREF: { - MethodInfo mi("weakref", PropertyInfo(Variant::OBJECT, "obj")); - mi.return_val.type = Variant::OBJECT; - mi.return_val.class_name = "WeakRef"; - - return mi; - - } break; - case TYPE_CONVERT: { - MethodInfo mi("convert", PropertyInfo(Variant::NIL, "what", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT), PropertyInfo(Variant::INT, "type")); - mi.return_val.type = Variant::NIL; - mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; - return mi; - } break; - case TYPE_OF: { - MethodInfo mi("typeof", PropertyInfo(Variant::NIL, "what", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT)); - mi.return_val.type = Variant::INT; - return mi; - - } break; - case TYPE_EXISTS: { - MethodInfo mi("type_exists", PropertyInfo(Variant::STRING, "type")); - mi.return_val.type = Variant::BOOL; - return mi; - - } break; - case TEXT_CHAR: { - MethodInfo mi("char", PropertyInfo(Variant::INT, "code")); - mi.return_val.type = Variant::STRING; - return mi; - - } break; - case TEXT_ORD: { - MethodInfo mi("ord", PropertyInfo(Variant::STRING, "char")); - mi.return_val.type = Variant::INT; - return mi; - - } break; - case TEXT_STR: { - MethodInfo mi("str"); - mi.return_val.type = Variant::STRING; - mi.flags |= METHOD_FLAG_VARARG; - return mi; - - } break; - case TEXT_PRINT: { - MethodInfo mi("print"); - mi.return_val.type = Variant::NIL; - mi.flags |= METHOD_FLAG_VARARG; - return mi; - - } break; - case TEXT_PRINT_TABBED: { - MethodInfo mi("printt"); - mi.return_val.type = Variant::NIL; - mi.flags |= METHOD_FLAG_VARARG; - return mi; - - } break; - case TEXT_PRINT_SPACED: { - MethodInfo mi("prints"); - mi.return_val.type = Variant::NIL; - mi.flags |= METHOD_FLAG_VARARG; - return mi; - - } break; - case TEXT_PRINTERR: { - MethodInfo mi("printerr"); - mi.return_val.type = Variant::NIL; - mi.flags |= METHOD_FLAG_VARARG; - return mi; - - } break; - case TEXT_PRINTRAW: { - MethodInfo mi("printraw"); - mi.return_val.type = Variant::NIL; - mi.flags |= METHOD_FLAG_VARARG; - return mi; - - } break; - case TEXT_PRINT_DEBUG: { - MethodInfo mi("print_debug"); - mi.return_val.type = Variant::NIL; - mi.flags |= METHOD_FLAG_VARARG; - return mi; - - } break; - case PUSH_ERROR: { - MethodInfo mi(Variant::NIL, "push_error", PropertyInfo(Variant::STRING, "message")); - mi.return_val.type = Variant::NIL; - return mi; - - } break; - case PUSH_WARNING: { - MethodInfo mi(Variant::NIL, "push_warning", PropertyInfo(Variant::STRING, "message")); - mi.return_val.type = Variant::NIL; - return mi; - - } break; - case VAR_TO_STR: { - MethodInfo mi("var2str", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT)); - mi.return_val.type = Variant::STRING; - return mi; - } break; - case STR_TO_VAR: { - MethodInfo mi(Variant::NIL, "str2var", PropertyInfo(Variant::STRING, "string")); - mi.return_val.type = Variant::NIL; - mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; - return mi; - } break; - case VAR_TO_BYTES: { - MethodInfo mi("var2bytes", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT), PropertyInfo(Variant::BOOL, "full_objects")); - mi.default_arguments.push_back(false); - mi.return_val.type = Variant::PACKED_BYTE_ARRAY; - return mi; - } break; - case BYTES_TO_VAR: { - MethodInfo mi(Variant::NIL, "bytes2var", PropertyInfo(Variant::PACKED_BYTE_ARRAY, "bytes"), PropertyInfo(Variant::BOOL, "allow_objects")); - mi.default_arguments.push_back(false); - mi.return_val.type = Variant::NIL; - mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; - return mi; - } break; - case GEN_RANGE: { - MethodInfo mi("range"); - mi.return_val.type = Variant::ARRAY; - mi.flags |= METHOD_FLAG_VARARG; - return mi; - } break; - case RESOURCE_LOAD: { - MethodInfo mi("load", PropertyInfo(Variant::STRING, "path")); - mi.return_val.type = Variant::OBJECT; - mi.return_val.class_name = "Resource"; - return mi; - } break; - case INST2DICT: { - MethodInfo mi("inst2dict", PropertyInfo(Variant::OBJECT, "inst")); - mi.return_val.type = Variant::DICTIONARY; - return mi; - } break; - case DICT2INST: { - MethodInfo mi("dict2inst", PropertyInfo(Variant::DICTIONARY, "dict")); - mi.return_val.type = Variant::OBJECT; - return mi; - } break; - case VALIDATE_JSON: { - MethodInfo mi("validate_json", PropertyInfo(Variant::STRING, "json")); - mi.return_val.type = Variant::STRING; - return mi; - } break; - case PARSE_JSON: { - MethodInfo mi(Variant::NIL, "parse_json", PropertyInfo(Variant::STRING, "json")); - mi.return_val.type = Variant::NIL; - mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; - return mi; - } break; - case TO_JSON: { - MethodInfo mi("to_json", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT)); - mi.return_val.type = Variant::STRING; - return mi; - } break; - case HASH: { - MethodInfo mi("hash", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT)); - mi.return_val.type = Variant::INT; - return mi; - } break; - case COLOR8: { - MethodInfo mi("Color8", PropertyInfo(Variant::INT, "r8"), PropertyInfo(Variant::INT, "g8"), PropertyInfo(Variant::INT, "b8"), PropertyInfo(Variant::INT, "a8")); - mi.default_arguments.push_back(255); - mi.return_val.type = Variant::COLOR; - return mi; - } break; - case COLORN: { - MethodInfo mi("ColorN", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::FLOAT, "alpha")); - mi.default_arguments.push_back(1.0f); - mi.return_val.type = Variant::COLOR; - return mi; - } break; - - case PRINT_STACK: { - MethodInfo mi("print_stack"); - mi.return_val.type = Variant::NIL; - return mi; - } break; - case GET_STACK: { - MethodInfo mi("get_stack"); - mi.return_val.type = Variant::ARRAY; - return mi; - } break; - - case INSTANCE_FROM_ID: { - MethodInfo mi("instance_from_id", PropertyInfo(Variant::INT, "instance_id")); - mi.return_val.type = Variant::OBJECT; - return mi; - } break; - case LEN: { - MethodInfo mi("len", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT)); - mi.return_val.type = Variant::INT; - return mi; - } break; - case IS_INSTANCE_VALID: { - MethodInfo mi("is_instance_valid", PropertyInfo(Variant::OBJECT, "instance")); - mi.return_val.type = Variant::BOOL; - return mi; - } break; - default: { - ERR_FAIL_V(MethodInfo()); - } break; - } -#endif - MethodInfo mi; - mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; - return mi; -} diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 372af204f5..a77fb14064 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -47,7 +47,7 @@ static HashMap<StringName, Variant::Type> builtin_types; Variant::Type GDScriptParser::get_builtin_type(const StringName &p_type) { - if (builtin_types.empty()) { + if (builtin_types.is_empty()) { builtin_types["bool"] = Variant::BOOL; builtin_types["int"] = Variant::INT; builtin_types["float"] = Variant::FLOAT; @@ -98,15 +98,6 @@ void GDScriptParser::cleanup() { builtin_types.clear(); } -GDScriptFunctions::Function GDScriptParser::get_builtin_function(const StringName &p_name) { - for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) { - if (p_name == GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i))) { - return GDScriptFunctions::Function(i); - } - } - return GDScriptFunctions::FUNC_MAX; -} - void GDScriptParser::get_annotation_list(List<MethodInfo> *r_annotations) const { List<StringName> keys; valid_annotations.get_key_list(&keys); @@ -186,16 +177,16 @@ void GDScriptParser::push_error(const String &p_message, const Node *p_origin) { void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_code, const String &p_symbol1, const String &p_symbol2, const String &p_symbol3, const String &p_symbol4) { ERR_FAIL_COND(p_source == nullptr); Vector<String> symbols; - if (!p_symbol1.empty()) { + if (!p_symbol1.is_empty()) { symbols.push_back(p_symbol1); } - if (!p_symbol2.empty()) { + if (!p_symbol2.is_empty()) { symbols.push_back(p_symbol2); } - if (!p_symbol3.empty()) { + if (!p_symbol3.is_empty()) { symbols.push_back(p_symbol3); } - if (!p_symbol4.empty()) { + if (!p_symbol4.is_empty()) { symbols.push_back(p_symbol4); } push_warning(p_source, p_code, symbols); @@ -293,7 +284,7 @@ void GDScriptParser::pop_completion_call() { if (!for_completion) { return; } - ERR_FAIL_COND_MSG(completion_call_stack.empty(), "Trying to pop empty completion call stack"); + ERR_FAIL_COND_MSG(completion_call_stack.is_empty(), "Trying to pop empty completion call stack"); completion_call_stack.pop_back(); } @@ -301,7 +292,7 @@ void GDScriptParser::set_last_completion_call_arg(int p_argument) { if (!for_completion || passed_cursor) { return; } - ERR_FAIL_COND_MSG(completion_call_stack.empty(), "Trying to set argument on empty completion call stack"); + ERR_FAIL_COND_MSG(completion_call_stack.is_empty(), "Trying to set argument on empty completion call stack"); completion_call_stack.back()->get().argument = p_argument; } @@ -367,7 +358,7 @@ Error GDScriptParser::parse(const String &p_source_code, const String &p_script_ } #endif - if (errors.empty()) { + if (errors.is_empty()) { return OK; } else { return ERR_PARSE_ERROR; @@ -378,7 +369,7 @@ GDScriptTokenizer::Token GDScriptParser::advance() { if (current.type == GDScriptTokenizer::Token::TK_EOF) { ERR_FAIL_COND_V_MSG(current.type == GDScriptTokenizer::Token::TK_EOF, current, "GDScript parser bug: Trying to advance past the end of stream."); } - if (for_completion && !completion_call_stack.empty()) { + if (for_completion && !completion_call_stack.is_empty()) { if (completion_call.call == nullptr && tokenizer.is_past_cursor()) { completion_call = completion_call_stack.back()->get(); passed_cursor = true; @@ -509,7 +500,7 @@ void GDScriptParser::parse_program() { // Order here doesn't matter, but there should be only one of each at most. switch (current.type) { case GDScriptTokenizer::Token::CLASS_NAME: - if (!annotation_stack.empty()) { + if (!annotation_stack.is_empty()) { push_error(R"("class_name" should be used before annotations.)"); } advance(); @@ -520,7 +511,7 @@ void GDScriptParser::parse_program() { } break; case GDScriptTokenizer::Token::EXTENDS: - if (!annotation_stack.empty()) { + if (!annotation_stack.is_empty()) { push_error(R"("extends" should be used before annotations.)"); } advance(); @@ -558,6 +549,17 @@ void GDScriptParser::parse_program() { parse_class_body(); +#ifdef TOOLS_ENABLED + for (Map<int, GDScriptTokenizer::CommentData>::Element *E = tokenizer.get_comments().front(); E; E = E->next()) { + if (E->get().new_line && E->get().comment.begins_with("##")) { + class_doc_line = MIN(class_doc_line, E->key()); + } + } + if (has_comment(class_doc_line)) { + get_class_doc_comment(class_doc_line, head->doc_brief_description, head->doc_description, head->doc_tutorials, false); + } +#endif // TOOLS_ENABLED + if (!check(GDScriptTokenizer::Token::TK_EOF)) { push_error("Expected end of file."); } @@ -668,8 +670,12 @@ void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)() if (member == nullptr) { return; } +#ifdef TOOLS_ENABLED + int doc_comment_line = member->start_line - 1; +#endif // TOOLS_ENABLED + // Consume annotations. - while (!annotation_stack.empty()) { + while (!annotation_stack.is_empty()) { AnnotationNode *last_annotation = annotation_stack.back()->get(); if (last_annotation->applies_to(p_target)) { last_annotation->apply(this, member); @@ -680,7 +686,25 @@ void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)() clear_unused_annotations(); return; } +#ifdef TOOLS_ENABLED + if (last_annotation->start_line == doc_comment_line) { + doc_comment_line--; + } +#endif // TOOLS_ENABLED + } + +#ifdef TOOLS_ENABLED + // Consume doc comments. + class_doc_line = MIN(class_doc_line, doc_comment_line - 1); + if (has_comment(doc_comment_line)) { + if constexpr (std::is_same_v<T, ClassNode>) { + get_class_doc_comment(doc_comment_line, member->doc_brief_description, member->doc_description, member->doc_tutorials, true); + } else { + member->doc_description = get_doc_comment(doc_comment_line); + } } +#endif // TOOLS_ENABLED + if (member->identifier != nullptr) { // Enums may be unnamed. // TODO: Consider names in outer scope too, for constants and classes (and static functions?) @@ -952,6 +976,8 @@ GDScriptParser::ConstantNode *GDScriptParser::parse_constant() { push_error(R"(Expected initializer expression for constant.)"); return nullptr; } + } else { + return nullptr; } end_statement("constant declaration"); @@ -1050,6 +1076,7 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() { item.parent_enum = enum_node; item.line = previous.start_line; item.leftmost_column = previous.leftmost_column; + item.rightmost_column = previous.rightmost_column; if (elements.has(item.identifier->name)) { push_error(vformat(R"(Name "%s" was already in this enum (at line %d).)", item.identifier->name, elements[item.identifier->name]), item.identifier); @@ -1088,6 +1115,31 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() { pop_multiline(); consume(GDScriptTokenizer::Token::BRACE_CLOSE, R"(Expected closing "}" for enum.)"); +#ifdef TOOLS_ENABLED + // Enum values documentaion. + for (int i = 0; i < enum_node->values.size(); i++) { + if (i == enum_node->values.size() - 1) { + // If close bracket is same line as last value. + if (enum_node->values[i].line != previous.start_line && has_comment(enum_node->values[i].line)) { + if (named) { + enum_node->values.write[i].doc_description = get_doc_comment(enum_node->values[i].line, true); + } else { + current_class->set_enum_value_doc(enum_node->values[i].identifier->name, get_doc_comment(enum_node->values[i].line, true)); + } + } + } else { + // If two values are same line. + if (enum_node->values[i].line != enum_node->values[i + 1].line && has_comment(enum_node->values[i].line)) { + if (named) { + enum_node->values.write[i].doc_description = get_doc_comment(enum_node->values[i].line, true); + } else { + current_class->set_enum_value_doc(enum_node->values[i].identifier->name, get_doc_comment(enum_node->values[i].line, true)); + } + } + } + } +#endif // TOOLS_ENABLED + end_statement("enum"); return enum_node; @@ -1451,12 +1503,9 @@ GDScriptParser::AssertNode *GDScriptParser::parse_assert() { if (match(GDScriptTokenizer::Token::COMMA)) { // Error message. - if (consume(GDScriptTokenizer::Token::LITERAL, R"(Expected error message for assert after ",".)")) { - assert->message = parse_literal(); - if (assert->message->value.get_type() != Variant::STRING) { - push_error(R"(Expected string for assert error message.)"); - } - } else { + assert->message = parse_expression(false); + if (assert->message == nullptr) { + push_error(R"(Expected error message for assert after ",".)"); return nullptr; } } @@ -1648,7 +1697,7 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() { branch->patterns.push_back(pattern); } while (match(GDScriptTokenizer::Token::COMMA)); - if (branch->patterns.empty()) { + if (branch->patterns.is_empty()) { push_error(R"(No pattern found for "match" branch.)"); } @@ -2494,7 +2543,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre // Arguments. CompletionType ct = COMPLETION_CALL_ARGUMENTS; - if (get_builtin_function(call->function_name) == GDScriptFunctions::RESOURCE_LOAD) { + if (call->function_name == "load") { ct = COMPLETION_RESOURCE_PATH; } push_completion_call(call); @@ -2624,6 +2673,218 @@ GDScriptParser::TypeNode *GDScriptParser::parse_type(bool p_allow_void) { return type; } +#ifdef TOOLS_ENABLED +static bool _in_codeblock(String p_line, bool p_already_in, int *r_block_begins = nullptr) { + int start_block = p_line.rfind("[codeblock]"); + int end_block = p_line.rfind("[/codeblock]"); + + if (start_block != -1 && r_block_begins) { + *r_block_begins = start_block; + } + + if (p_already_in) { + if (end_block == -1) { + return true; + } else if (start_block == -1) { + return false; + } else { + return start_block > end_block; + } + } else { + if (start_block == -1) { + return false; + } else if (end_block == -1) { + return true; + } else { + return start_block > end_block; + } + } +} + +bool GDScriptParser::has_comment(int p_line) { + return tokenizer.get_comments().has(p_line); +} + +String GDScriptParser::get_doc_comment(int p_line, bool p_single_line) { + const Map<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments(); + ERR_FAIL_COND_V(!comments.has(p_line), String()); + + if (p_single_line) { + if (comments[p_line].comment.begins_with("##")) { + return comments[p_line].comment.trim_prefix("##").strip_edges(); + } + return ""; + } + + String doc; + + int line = p_line; + bool in_codeblock = false; + + while (comments.has(line - 1)) { + if (!comments[line - 1].new_line || !comments[line - 1].comment.begins_with("##")) { + break; + } + line--; + } + + int codeblock_begins = 0; + while (comments.has(line)) { + if (!comments[line].new_line || !comments[line].comment.begins_with("##")) { + break; + } + String doc_line = comments[line].comment.trim_prefix("##"); + + in_codeblock = _in_codeblock(doc_line, in_codeblock, &codeblock_begins); + + if (in_codeblock) { + int i = 0; + for (; i < codeblock_begins; i++) { + if (doc_line[i] != ' ') { + break; + } + } + doc_line = doc_line.substr(i); + } else { + doc_line = doc_line.strip_edges(); + } + String line_join = (in_codeblock) ? "\n" : " "; + + doc = (doc.is_empty()) ? doc_line : doc + line_join + doc_line; + line++; + } + + return doc; +} + +void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String &p_desc, Vector<Pair<String, String>> &p_tutorials, bool p_inner_class) { + const Map<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments(); + if (!comments.has(p_line)) { + return; + } + ERR_FAIL_COND(p_brief != "" || p_desc != "" || p_tutorials.size() != 0); + + int line = p_line; + bool in_codeblock = false; + enum Mode { + BRIEF, + DESC, + TUTORIALS, + DONE, + }; + Mode mode = BRIEF; + + if (p_inner_class) { + while (comments.has(line - 1)) { + if (!comments[line - 1].new_line || !comments[line - 1].comment.begins_with("##")) { + break; + } + line--; + } + } + + int codeblock_begins = 0; + while (comments.has(line)) { + if (!comments[line].new_line || !comments[line].comment.begins_with("##")) { + break; + } + + String title, link; // For tutorials. + String doc_line = comments[line++].comment.trim_prefix("##"); + String striped_line = doc_line.strip_edges(); + + // Set the read mode. + if (striped_line.begins_with("@desc:") && p_desc == "") { + mode = DESC; + striped_line = striped_line.trim_prefix("@desc:"); + in_codeblock = _in_codeblock(doc_line, in_codeblock); + + } else if (striped_line.begins_with("@tutorial")) { + int begin_scan = String("@tutorial").length(); + if (begin_scan >= striped_line.length()) { + continue; // invalid syntax. + } + + if (striped_line[begin_scan] == ':') { // No title. + // Syntax: ## @tutorial: https://godotengine.org/ // The title argument is optional. + title = ""; + link = striped_line.trim_prefix("@tutorial:").strip_edges(); + + } else { + /* Syntax: + @tutorial ( The Title Here ) : http://the.url/ + ^ open ^ close ^ colon ^ url + */ + int open_bracket_pos = begin_scan, close_bracket_pos = 0; + while (open_bracket_pos < striped_line.length() && (striped_line[open_bracket_pos] == ' ' || striped_line[open_bracket_pos] == '\t')) { + open_bracket_pos++; + } + if (open_bracket_pos == striped_line.length() || striped_line[open_bracket_pos++] != '(') { + continue; // invalid syntax. + } + close_bracket_pos = open_bracket_pos; + while (close_bracket_pos < striped_line.length() && striped_line[close_bracket_pos] != ')') { + close_bracket_pos++; + } + if (close_bracket_pos == striped_line.length()) { + continue; // invalid syntax. + } + + int colon_pos = close_bracket_pos + 1; + while (colon_pos < striped_line.length() && (striped_line[colon_pos] == ' ' || striped_line[colon_pos] == '\t')) { + colon_pos++; + } + if (colon_pos == striped_line.length() || striped_line[colon_pos++] != ':') { + continue; // invalid syntax. + } + + title = striped_line.substr(open_bracket_pos, close_bracket_pos - open_bracket_pos).strip_edges(); + link = striped_line.substr(colon_pos).strip_edges(); + } + + mode = TUTORIALS; + in_codeblock = false; + } else if (striped_line.is_empty()) { + continue; + } else { + // Tutorial docs are single line, we need a @tag after it. + if (mode == TUTORIALS) { + mode = DONE; + } + + in_codeblock = _in_codeblock(doc_line, in_codeblock, &codeblock_begins); + } + + if (in_codeblock) { + int i = 0; + for (; i < codeblock_begins; i++) { + if (doc_line[i] != ' ') { + break; + } + } + doc_line = doc_line.substr(i); + } else { + doc_line = striped_line; + } + String line_join = (in_codeblock) ? "\n" : " "; + + switch (mode) { + case BRIEF: + p_brief = (p_brief.length() == 0) ? doc_line : p_brief + line_join + doc_line; + break; + case DESC: + p_desc = (p_desc.length() == 0) ? doc_line : p_desc + line_join + doc_line; + break; + case TUTORIALS: + p_tutorials.append(Pair<String, String>(title, link)); + break; + case DONE: + return; + } + } +} +#endif // TOOLS_ENABLED + GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Type p_token_type) { // Function table for expression parsing. // clang-format destroys the alignment here, so turn off for the table. @@ -2657,8 +2918,8 @@ GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Ty { nullptr, &GDScriptParser::parse_binary_operator, PREC_BIT_SHIFT }, // LESS_LESS, { nullptr, &GDScriptParser::parse_binary_operator, PREC_BIT_SHIFT }, // GREATER_GREATER, // Math - { &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_operator, PREC_ADDITION }, // PLUS, - { &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_operator, PREC_SUBTRACTION }, // MINUS, + { &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_operator, PREC_ADDITION_SUBTRACTION }, // PLUS, + { &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_operator, PREC_ADDITION_SUBTRACTION }, // MINUS, { nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // STAR, { nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // SLASH, { nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // PERCENT, @@ -3018,11 +3279,11 @@ String GDScriptParser::DataType::to_string() const { return script_type->get_class_name().operator String(); } String name = script_type->get_name(); - if (!name.empty()) { + if (!name.is_empty()) { return name; } name = script_path; - if (!name.empty()) { + if (!name.is_empty()) { return name; } return native_type.operator String(); @@ -3067,7 +3328,7 @@ void GDScriptParser::TreePrinter::decrease_indent() { } void GDScriptParser::TreePrinter::push_line(const String &p_line) { - if (!p_line.empty()) { + if (!p_line.is_empty()) { push_text(p_line); } printed += "\n"; @@ -3276,7 +3537,7 @@ void GDScriptParser::TreePrinter::print_class(ClassNode *p_class) { if (p_class->extends_used) { bool first = true; push_text(" Extends "); - if (!p_class->extends_path.empty()) { + if (!p_class->extends_path.is_empty()) { push_text(vformat(R"("%s")", p_class->extends_path)); first = false; } @@ -3738,7 +3999,7 @@ void GDScriptParser::TreePrinter::print_ternary_op(TernaryOpNode *p_ternary_op) } void GDScriptParser::TreePrinter::print_type(TypeNode *p_type) { - if (p_type->type_chain.empty()) { + if (p_type->type_chain.is_empty()) { push_text("Void"); } else { for (int i = 0; i < p_type->type_chain.size(); i++) { @@ -3858,7 +4119,7 @@ void GDScriptParser::TreePrinter::print_tree(const GDScriptParser &p_parser) { if (p_parser.is_tool()) { push_line("@tool"); } - if (!p_parser.get_tree()->icon_path.empty()) { + if (!p_parser.get_tree()->icon_path.is_empty()) { push_text(R"(@icon (")"); push_text(p_parser.get_tree()->icon_path); push_line("\")"); diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index b24acc4778..f43708b81f 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -43,7 +43,6 @@ #include "core/templates/vector.h" #include "core/variant/variant.h" #include "gdscript_cache.h" -#include "gdscript_functions.h" #include "gdscript_tokenizer.h" #ifdef DEBUG_ENABLED @@ -287,7 +286,7 @@ public: struct AssertNode : public Node { ExpressionNode *condition = nullptr; - LiteralNode *message = nullptr; + ExpressionNode *message = nullptr; AssertNode() { type = ASSERT; @@ -413,9 +412,16 @@ public: int line = 0; int leftmost_column = 0; int rightmost_column = 0; +#ifdef TOOLS_ENABLED + String doc_description; +#endif // TOOLS_ENABLED }; + IdentifierNode *identifier = nullptr; Vector<Value> values; +#ifdef TOOLS_ENABLED + String doc_description; +#endif // TOOLS_ENABLED EnumNode() { type = ENUM; @@ -568,6 +574,17 @@ public: Vector<StringName> extends; // List for indexing: extends A.B.C DataType base_type; String fqcn; // Fully-qualified class name. Identifies uniquely any class in the project. +#ifdef TOOLS_ENABLED + String doc_description; + String doc_brief_description; + Vector<Pair<String, String>> doc_tutorials; + + // EnumValue docs are parsed after itself, so we need a method to add/modify the doc property later. + void set_enum_value_doc(const StringName &p_name, const String &p_doc_description) { + ERR_FAIL_INDEX(members_indices[p_name], members.size()); + members.write[members_indices[p_name]].enum_value.doc_description = p_doc_description; + } +#endif // TOOLS_ENABLED bool resolved_interface = false; bool resolved_body = false; @@ -602,6 +619,9 @@ public: TypeNode *datatype_specifier = nullptr; bool infer_datatype = false; int usages = 0; +#ifdef TOOLS_ENABLED + String doc_description; +#endif // TOOLS_ENABLED ConstantNode() { type = CONSTANT; @@ -653,6 +673,10 @@ public: bool is_coroutine = false; MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED; MethodInfo info; +#ifdef TOOLS_ENABLED + Vector<Variant> default_arg_values; + String doc_description; +#endif // TOOLS_ENABLED bool resolved_signature = false; bool resolved_body = false; @@ -820,6 +844,9 @@ public: IdentifierNode *identifier = nullptr; Vector<ParameterNode *> parameters; HashMap<StringName, int> parameters_indices; +#ifdef TOOLS_ENABLED + String doc_description; +#endif // TOOLS_ENABLED SignalNode() { type = SIGNAL; @@ -1012,6 +1039,9 @@ public: MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED; int assignments = 0; int usages = 0; +#ifdef TOOLS_ENABLED + String doc_description; +#endif // TOOLS_ENABLED VariableNode() { type = VARIABLE; @@ -1142,8 +1172,7 @@ private: PREC_BIT_XOR, PREC_BIT_AND, PREC_BIT_SHIFT, - PREC_SUBTRACTION, - PREC_ADDITION, + PREC_ADDITION_SUBTRACTION, PREC_FACTOR, PREC_SIGN, PREC_BIT_NOT, @@ -1270,13 +1299,19 @@ private: ExpressionNode *parse_subscript(ExpressionNode *p_previous_operand, bool p_can_assign); ExpressionNode *parse_invalid_token(ExpressionNode *p_previous_operand, bool p_can_assign); TypeNode *parse_type(bool p_allow_void = false); +#ifdef TOOLS_ENABLED + // Doc comments. + int class_doc_line = 0x7FFFFFFF; + bool has_comment(int p_line); + String get_doc_comment(int p_line, bool p_single_line = false); + void get_class_doc_comment(int p_line, String &p_brief, String &p_desc, Vector<Pair<String, String>> &p_tutorials, bool p_inner_class); +#endif // TOOLS_ENABLED public: Error parse(const String &p_source_code, const String &p_script_path, bool p_for_completion); ClassNode *get_tree() const { return head; } bool is_tool() const { return _is_tool; } static Variant::Type get_builtin_type(const StringName &p_type); - static GDScriptFunctions::Function get_builtin_function(const StringName &p_name); CompletionContext get_completion_context() const { return completion_context; } CompletionCall get_completion_call() const { return completion_call; } diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index b91777ede1..315b8ee3b4 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -221,7 +221,7 @@ String GDScriptTokenizer::get_token_name(Token::Type p_token_type) { void GDScriptTokenizer::set_source_code(const String &p_source_code) { source = p_source_code; - if (source.empty()) { + if (source.is_empty()) { _source = U""; } else { _source = source.ptr(); @@ -287,7 +287,7 @@ void GDScriptTokenizer::push_paren(char32_t p_char) { } bool GDScriptTokenizer::pop_paren(char32_t p_expected) { - if (paren_stack.empty()) { + if (paren_stack.is_empty()) { return false; } char32_t actual = paren_stack.back()->get(); @@ -405,7 +405,7 @@ void GDScriptTokenizer::push_error(const Token &p_error) { } GDScriptTokenizer::Token GDScriptTokenizer::make_paren_error(char32_t p_paren) { - if (paren_stack.empty()) { + if (paren_stack.is_empty()) { return make_error(vformat("Closing \"%c\" doesn't have an opening counterpart.", p_paren)); } Token error = make_error(vformat("Closing \"%c\" doesn't match the opening \"%c\".", p_paren, paren_stack.back()->get())); @@ -1014,9 +1014,17 @@ void GDScriptTokenizer::check_indent() { } if (_peek() == '#') { // Comment. Advance to the next line. +#ifdef TOOLS_ENABLED + String comment; + while (_peek() != '\n' && !_is_at_end()) { + comment += _advance(); + } + comments[line] = CommentData(comment, true); +#else while (_peek() != '\n' && !_is_at_end()) { _advance(); } +#endif // TOOLS_ENABLED if (_is_at_end()) { // Reached the end with an empty line, so just dedent as much as needed. pending_indents -= indent_level(); @@ -1125,18 +1133,26 @@ void GDScriptTokenizer::_skip_whitespace() { newline(!is_bol); // Don't create new line token if line is empty. check_indent(); break; - case '#': + case '#': { // Comment. +#ifdef TOOLS_ENABLED + String comment; + while (_peek() != '\n' && !_is_at_end()) { + comment += _advance(); + } + comments[line] = CommentData(comment, is_bol); +#else while (_peek() != '\n' && !_is_at_end()) { _advance(); } +#endif // TOOLS_ENABLED if (_is_at_end()) { return; } _advance(); // Consume '\n' newline(!is_bol); check_indent(); - break; + } break; default: return; } diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h index d51f1f250f..cdb0072294 100644 --- a/modules/gdscript/gdscript_tokenizer.h +++ b/modules/gdscript/gdscript_tokenizer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,6 +32,7 @@ #define GDSCRIPT_TOKENIZER_H #include "core/templates/list.h" +#include "core/templates/map.h" #include "core/templates/set.h" #include "core/templates/vector.h" #include "core/variant/variant.h" @@ -181,6 +182,21 @@ public: } }; +#ifdef TOOLS_ENABLED + struct CommentData { + String comment; + bool new_line = false; + CommentData() {} + CommentData(const String &p_comment, bool p_new_line) { + comment = p_comment; + new_line = p_new_line; + } + }; + const Map<int, CommentData> &get_comments() const { + return comments; + } +#endif // TOOLS_ENABLED + private: String source; const char32_t *_source = nullptr; @@ -207,10 +223,14 @@ private: int position = 0; int length = 0; +#ifdef TOOLS_ENABLED + Map<int, CommentData> comments; +#endif // TOOLS_ENABLED + _FORCE_INLINE_ bool _is_at_end() { return position >= length; } _FORCE_INLINE_ char32_t _peek(int p_offset = 0) { return position + p_offset >= 0 && position + p_offset < length ? _current[p_offset] : '\0'; } int indent_level() const { return indent_stack.size(); } - bool has_error() const { return !error_stack.empty(); } + bool has_error() const { return !error_stack.is_empty(); } Token pop_error(); char32_t _advance(); void _skip_whitespace(); diff --git a/modules/gdscript/gdscript_utility_functions.cpp b/modules/gdscript/gdscript_utility_functions.cpp new file mode 100644 index 0000000000..348d221352 --- /dev/null +++ b/modules/gdscript/gdscript_utility_functions.cpp @@ -0,0 +1,718 @@ +/*************************************************************************/ +/* gdscript_utility_functions.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "gdscript_utility_functions.h" + +#include "core/io/resource_loader.h" +#include "core/object/class_db.h" +#include "core/object/method_bind.h" +#include "core/object/object.h" +#include "core/templates/oa_hash_map.h" +#include "core/templates/vector.h" +#include "gdscript.h" + +#ifdef DEBUG_ENABLED + +#define VALIDATE_ARG_COUNT(m_count) \ + if (p_arg_count < m_count) { \ + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; \ + r_error.argument = m_count; \ + r_error.expected = m_count; \ + *r_ret = Variant(); \ + return; \ + } \ + if (p_arg_count > m_count) { \ + r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; \ + r_error.argument = m_count; \ + r_error.expected = m_count; \ + *r_ret = Variant(); \ + return; \ + } + +#define VALIDATE_ARG_INT(m_arg) \ + if (p_args[m_arg]->get_type() != Variant::INT) { \ + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \ + r_error.argument = m_arg; \ + r_error.expected = Variant::INT; \ + *r_ret = Variant(); \ + return; \ + } + +#define VALIDATE_ARG_NUM(m_arg) \ + if (!p_args[m_arg]->is_num()) { \ + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \ + r_error.argument = m_arg; \ + r_error.expected = Variant::FLOAT; \ + *r_ret = Variant(); \ + return; \ + } + +#else + +#define VALIDATE_ARG_COUNT(m_count) +#define VALIDATE_ARG_INT(m_arg) +#define VALIDATE_ARG_NUM(m_arg) + +#endif + +struct GDScriptUtilityFunctionsDefinitions { + static inline void convert(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + VALIDATE_ARG_COUNT(2); + VALIDATE_ARG_INT(1); + int type = *p_args[1]; + if (type < 0 || type >= Variant::VARIANT_MAX) { + *r_ret = RTR("Invalid type argument to convert(), use TYPE_* constants."); + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::INT; + return; + + } else { + Variant::construct(Variant::Type(type), *r_ret, p_args, 1, r_error); + } + } + + static inline void type_exists(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + VALIDATE_ARG_COUNT(1); + *r_ret = ClassDB::class_exists(*p_args[0]); + } + + static inline void _char(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + VALIDATE_ARG_COUNT(1); + VALIDATE_ARG_INT(0); + char32_t result[2] = { *p_args[0], 0 }; + *r_ret = String(result); + } + + static inline void str(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + if (p_arg_count < 1) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 1; + *r_ret = Variant(); + return; + } + + String str; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); + + if (i == 0) { + str = os; + } else { + str += os; + } + } + *r_ret = str; + } + + static inline void range(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + switch (p_arg_count) { + case 0: { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 1; + r_error.expected = 1; + *r_ret = Variant(); + } break; + case 1: { + VALIDATE_ARG_NUM(0); + int count = *p_args[0]; + Array arr; + if (count <= 0) { + *r_ret = arr; + return; + } + Error err = arr.resize(count); + if (err != OK) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + *r_ret = Variant(); + return; + } + + for (int i = 0; i < count; i++) { + arr[i] = i; + } + + *r_ret = arr; + } break; + case 2: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + + int from = *p_args[0]; + int to = *p_args[1]; + + Array arr; + if (from >= to) { + *r_ret = arr; + return; + } + Error err = arr.resize(to - from); + if (err != OK) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + *r_ret = Variant(); + return; + } + for (int i = from; i < to; i++) { + arr[i - from] = i; + } + *r_ret = arr; + } break; + case 3: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + + int from = *p_args[0]; + int to = *p_args[1]; + int incr = *p_args[2]; + if (incr == 0) { + *r_ret = RTR("Step argument is zero!"); + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return; + } + + Array arr; + if (from >= to && incr > 0) { + *r_ret = arr; + return; + } + if (from <= to && incr < 0) { + *r_ret = arr; + return; + } + + // Calculate how many. + int count = 0; + if (incr > 0) { + count = ((to - from - 1) / incr) + 1; + } else { + count = ((from - to - 1) / -incr) + 1; + } + + Error err = arr.resize(count); + + if (err != OK) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + *r_ret = Variant(); + return; + } + + if (incr > 0) { + int idx = 0; + for (int i = from; i < to; i += incr) { + arr[idx++] = i; + } + } else { + int idx = 0; + for (int i = from; i > to; i += incr) { + arr[idx++] = i; + } + } + + *r_ret = arr; + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; + r_error.argument = 3; + r_error.expected = 3; + *r_ret = Variant(); + + } break; + } + } + + static inline void load(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + VALIDATE_ARG_COUNT(1); + if (p_args[0]->get_type() != Variant::STRING) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::STRING; + *r_ret = Variant(); + } else { + *r_ret = ResourceLoader::load(*p_args[0]); + } + } + + static inline void inst2dict(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + VALIDATE_ARG_COUNT(1); + + if (p_args[0]->get_type() == Variant::NIL) { + *r_ret = Variant(); + } else if (p_args[0]->get_type() != Variant::OBJECT) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + *r_ret = Variant(); + } else { + Object *obj = *p_args[0]; + if (!obj) { + *r_ret = Variant(); + + } else if (!obj->get_script_instance() || obj->get_script_instance()->get_language() != GDScriptLanguage::get_singleton()) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::DICTIONARY; + *r_ret = RTR("Not a script with an instance"); + return; + } else { + GDScriptInstance *ins = static_cast<GDScriptInstance *>(obj->get_script_instance()); + Ref<GDScript> base = ins->get_script(); + if (base.is_null()) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::DICTIONARY; + *r_ret = RTR("Not based on a script"); + return; + } + + GDScript *p = base.ptr(); + Vector<StringName> sname; + + while (p->_owner) { + sname.push_back(p->name); + p = p->_owner; + } + sname.invert(); + + if (!p->path.is_resource_file()) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::DICTIONARY; + *r_ret = Variant(); + + *r_ret = RTR("Not based on a resource file"); + + return; + } + + NodePath cp(sname, Vector<StringName>(), false); + + Dictionary d; + d["@subpath"] = cp; + d["@path"] = p->get_path(); + + for (Map<StringName, GDScript::MemberInfo>::Element *E = base->member_indices.front(); E; E = E->next()) { + if (!d.has(E->key())) { + d[E->key()] = ins->members[E->get().index]; + } + } + *r_ret = d; + } + } + } + + static inline void dict2inst(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + VALIDATE_ARG_COUNT(1); + + if (p_args[0]->get_type() != Variant::DICTIONARY) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::DICTIONARY; + *r_ret = Variant(); + + return; + } + + Dictionary d = *p_args[0]; + + if (!d.has("@path")) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::OBJECT; + *r_ret = RTR("Invalid instance dictionary format (missing @path)"); + + return; + } + + Ref<Script> scr = ResourceLoader::load(d["@path"]); + if (!scr.is_valid()) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::OBJECT; + *r_ret = RTR("Invalid instance dictionary format (can't load script at @path)"); + return; + } + + Ref<GDScript> gdscr = scr; + + if (!gdscr.is_valid()) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::OBJECT; + *r_ret = Variant(); + *r_ret = RTR("Invalid instance dictionary format (invalid script at @path)"); + return; + } + + NodePath sub; + if (d.has("@subpath")) { + sub = d["@subpath"]; + } + + for (int i = 0; i < sub.get_name_count(); i++) { + gdscr = gdscr->subclasses[sub.get_name(i)]; + if (!gdscr.is_valid()) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::OBJECT; + *r_ret = Variant(); + *r_ret = RTR("Invalid instance dictionary (invalid subclasses)"); + return; + } + } + *r_ret = gdscr->_new(nullptr, -1 /*skip initializer*/, r_error); + + if (r_error.error != Callable::CallError::CALL_OK) { + *r_ret = Variant(); + return; + } + + GDScriptInstance *ins = static_cast<GDScriptInstance *>(static_cast<Object *>(*r_ret)->get_script_instance()); + Ref<GDScript> gd_ref = ins->get_script(); + + for (Map<StringName, GDScript::MemberInfo>::Element *E = gd_ref->member_indices.front(); E; E = E->next()) { + if (d.has(E->key())) { + ins->members.write[E->get().index] = d[E->key()]; + } + } + } + + static inline void Color8(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + if (p_arg_count < 3) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 3; + *r_ret = Variant(); + return; + } + if (p_arg_count > 4) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; + r_error.argument = 4; + *r_ret = Variant(); + return; + } + + VALIDATE_ARG_INT(0); + VALIDATE_ARG_INT(1); + VALIDATE_ARG_INT(2); + + Color color((int64_t)*p_args[0] / 255.0f, (int64_t)*p_args[1] / 255.0f, (int64_t)*p_args[2] / 255.0f); + + if (p_arg_count == 4) { + VALIDATE_ARG_INT(3); + color.a = (int64_t)*p_args[3] / 255.0f; + } + + *r_ret = color; + } + + static inline void print_debug(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + String str; + for (int i = 0; i < p_arg_count; i++) { + str += p_args[i]->operator String(); + } + + ScriptLanguage *script = GDScriptLanguage::get_singleton(); + if (script->debug_get_stack_level_count() > 0) { + str += "\n At: " + script->debug_get_stack_level_source(0) + ":" + itos(script->debug_get_stack_level_line(0)) + ":" + script->debug_get_stack_level_function(0) + "()"; + } + + print_line(str); + *r_ret = Variant(); + } + + static inline void print_stack(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + VALIDATE_ARG_COUNT(0); + + ScriptLanguage *script = GDScriptLanguage::get_singleton(); + for (int i = 0; i < script->debug_get_stack_level_count(); i++) { + print_line("Frame " + itos(i) + " - " + script->debug_get_stack_level_source(i) + ":" + itos(script->debug_get_stack_level_line(i)) + " in function '" + script->debug_get_stack_level_function(i) + "'"); + }; + } + + static inline void get_stack(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + VALIDATE_ARG_COUNT(0); + + ScriptLanguage *script = GDScriptLanguage::get_singleton(); + Array ret; + for (int i = 0; i < script->debug_get_stack_level_count(); i++) { + Dictionary frame; + frame["source"] = script->debug_get_stack_level_source(i); + frame["function"] = script->debug_get_stack_level_function(i); + frame["line"] = script->debug_get_stack_level_line(i); + ret.push_back(frame); + }; + *r_ret = ret; + } + + static inline void len(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + VALIDATE_ARG_COUNT(1); + switch (p_args[0]->get_type()) { + case Variant::STRING: { + String d = *p_args[0]; + *r_ret = d.length(); + } break; + case Variant::DICTIONARY: { + Dictionary d = *p_args[0]; + *r_ret = d.size(); + } break; + case Variant::ARRAY: { + Array d = *p_args[0]; + *r_ret = d.size(); + } break; + case Variant::PACKED_BYTE_ARRAY: { + Vector<uint8_t> d = *p_args[0]; + *r_ret = d.size(); + } break; + case Variant::PACKED_INT32_ARRAY: { + Vector<int32_t> d = *p_args[0]; + *r_ret = d.size(); + } break; + case Variant::PACKED_INT64_ARRAY: { + Vector<int64_t> d = *p_args[0]; + *r_ret = d.size(); + } break; + case Variant::PACKED_FLOAT32_ARRAY: { + Vector<float> d = *p_args[0]; + *r_ret = d.size(); + } break; + case Variant::PACKED_FLOAT64_ARRAY: { + Vector<double> d = *p_args[0]; + *r_ret = d.size(); + } break; + case Variant::PACKED_STRING_ARRAY: { + Vector<String> d = *p_args[0]; + *r_ret = d.size(); + } break; + case Variant::PACKED_VECTOR2_ARRAY: { + Vector<Vector2> d = *p_args[0]; + *r_ret = d.size(); + } break; + case Variant::PACKED_VECTOR3_ARRAY: { + Vector<Vector3> d = *p_args[0]; + *r_ret = d.size(); + } break; + case Variant::PACKED_COLOR_ARRAY: { + Vector<Color> d = *p_args[0]; + *r_ret = d.size(); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::NIL; + *r_ret = vformat(RTR("Value of type '%s' can't provide a length."), Variant::get_type_name(p_args[0]->get_type())); + } + } + } +}; + +struct GDScriptUtilityFunctionInfo { + GDScriptUtilityFunctions::FunctionPtr function; + MethodInfo info; + bool is_constant = false; +}; + +static OAHashMap<StringName, GDScriptUtilityFunctionInfo> utility_function_table; +static List<StringName> utility_function_name_table; + +static void _register_function(const String &p_name, const MethodInfo &p_method_info, GDScriptUtilityFunctions::FunctionPtr p_function, bool p_is_const) { + StringName sname(p_name); + + ERR_FAIL_COND(utility_function_table.has(sname)); + + GDScriptUtilityFunctionInfo function; + function.function = p_function; + function.info = p_method_info; + function.is_constant = p_is_const; + + utility_function_table.insert(sname, function); + utility_function_name_table.push_back(sname); +} + +#define REGISTER_FUNC(m_func, m_is_const, m_return_type, ...) \ + { \ + String name(#m_func); \ + if (name.begins_with("_")) { \ + name = name.substr(1, name.length() - 1); \ + } \ + MethodInfo info = MethodInfo(name, __VA_ARGS__); \ + info.return_val.type = m_return_type; \ + _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \ + } + +#define REGISTER_FUNC_NO_ARGS(m_func, m_is_const, m_return_type) \ + { \ + String name(#m_func); \ + if (name.begins_with("_")) { \ + name = name.substr(1, name.length() - 1); \ + } \ + MethodInfo info = MethodInfo(name); \ + info.return_val.type = m_return_type; \ + _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \ + } + +#define REGISTER_VARARG_FUNC(m_func, m_is_const, m_return_type) \ + { \ + String name(#m_func); \ + if (name.begins_with("_")) { \ + name = name.substr(1, name.length() - 1); \ + } \ + MethodInfo info = MethodInfo(name); \ + info.return_val.type = m_return_type; \ + info.flags |= METHOD_FLAG_VARARG; \ + _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \ + } + +#define REGISTER_VARIANT_FUNC(m_func, m_is_const, ...) \ + { \ + String name(#m_func); \ + if (name.begins_with("_")) { \ + name = name.substr(1, name.length() - 1); \ + } \ + MethodInfo info = MethodInfo(name, __VA_ARGS__); \ + info.return_val.type = Variant::NIL; \ + info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; \ + _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \ + } + +#define REGISTER_CLASS_FUNC(m_func, m_is_const, m_return_type, ...) \ + { \ + String name(#m_func); \ + if (name.begins_with("_")) { \ + name = name.substr(1, name.length() - 1); \ + } \ + MethodInfo info = MethodInfo(name, __VA_ARGS__); \ + info.return_val.type = Variant::OBJECT; \ + info.return_val.hint = PROPERTY_HINT_RESOURCE_TYPE; \ + info.return_val.class_name = m_return_type; \ + _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \ + } + +#define REGISTER_FUNC_DEF(m_func, m_is_const, m_default, m_return_type, ...) \ + { \ + String name(#m_func); \ + if (name.begins_with("_")) { \ + name = name.substr(1, name.length() - 1); \ + } \ + MethodInfo info = MethodInfo(name, __VA_ARGS__); \ + info.return_val.type = m_return_type; \ + info.default_arguments.push_back(m_default); \ + _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \ + } + +#define ARG(m_name, m_type) \ + PropertyInfo(m_type, m_name) + +#define VARARG(m_name) \ + PropertyInfo(Variant::NIL, m_name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT) + +void GDScriptUtilityFunctions::register_functions() { + REGISTER_VARIANT_FUNC(convert, true, VARARG("what"), ARG("type", Variant::INT)); + REGISTER_FUNC(type_exists, true, Variant::BOOL, ARG("type", Variant::STRING_NAME)); + REGISTER_FUNC(_char, true, Variant::STRING, ARG("char", Variant::INT)); + REGISTER_VARARG_FUNC(str, true, Variant::STRING); + REGISTER_VARARG_FUNC(range, false, Variant::ARRAY); + REGISTER_CLASS_FUNC(load, false, "Resource", ARG("path", Variant::STRING)); + REGISTER_FUNC(inst2dict, false, Variant::DICTIONARY, ARG("instance", Variant::OBJECT)); + REGISTER_FUNC(dict2inst, false, Variant::OBJECT, ARG("dictionary", Variant::DICTIONARY)); + REGISTER_FUNC_DEF(Color8, true, 255, Variant::COLOR, ARG("r8", Variant::INT), ARG("g8", Variant::INT), ARG("b8", Variant::INT), ARG("a8", Variant::INT)); + REGISTER_VARARG_FUNC(print_debug, false, Variant::NIL); + REGISTER_FUNC_NO_ARGS(print_stack, false, Variant::NIL); + REGISTER_FUNC_NO_ARGS(get_stack, false, Variant::ARRAY); + REGISTER_FUNC(len, true, Variant::INT, VARARG("var")); +} + +void GDScriptUtilityFunctions::unregister_functions() { + utility_function_name_table.clear(); + utility_function_table.clear(); +} + +GDScriptUtilityFunctions::FunctionPtr GDScriptUtilityFunctions::get_function(const StringName &p_function) { + GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function); + ERR_FAIL_COND_V(!info, nullptr); + return info->function; +} + +bool GDScriptUtilityFunctions::has_function_return_value(const StringName &p_function) { + GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function); + ERR_FAIL_COND_V(!info, false); + return info->info.return_val.type != Variant::NIL || bool(info->info.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT); +} + +Variant::Type GDScriptUtilityFunctions::get_function_return_type(const StringName &p_function) { + GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function); + ERR_FAIL_COND_V(!info, Variant::NIL); + return info->info.return_val.type; +} + +StringName GDScriptUtilityFunctions::get_function_return_class(const StringName &p_function) { + GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function); + ERR_FAIL_COND_V(!info, StringName()); + return info->info.return_val.class_name; +} + +Variant::Type GDScriptUtilityFunctions::get_function_argument_type(const StringName &p_function, int p_arg) { + GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function); + ERR_FAIL_COND_V(!info, Variant::NIL); + ERR_FAIL_COND_V(p_arg >= info->info.arguments.size(), Variant::NIL); + return info->info.arguments[p_arg].type; +} + +int GDScriptUtilityFunctions::get_function_argument_count(const StringName &p_function, int p_arg) { + GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function); + ERR_FAIL_COND_V(!info, 0); + return info->info.arguments.size(); +} + +bool GDScriptUtilityFunctions::is_function_vararg(const StringName &p_function) { + GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function); + ERR_FAIL_COND_V(!info, false); + return (bool)(info->info.flags & METHOD_FLAG_VARARG); +} + +bool GDScriptUtilityFunctions::is_function_constant(const StringName &p_function) { + GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function); + ERR_FAIL_COND_V(!info, false); + return info->is_constant; +} + +bool GDScriptUtilityFunctions::function_exists(const StringName &p_function) { + return utility_function_table.has(p_function); +} + +void GDScriptUtilityFunctions::get_function_list(List<StringName> *r_functions) { + for (const List<StringName>::Element *E = utility_function_name_table.front(); E; E = E->next()) { + r_functions->push_back(E->get()); + } +} + +MethodInfo GDScriptUtilityFunctions::get_function_info(const StringName &p_function) { + GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function); + ERR_FAIL_COND_V(!info, MethodInfo()); + return info->info; +} diff --git a/modules/gdscript/gdscript_functions.h b/modules/gdscript/gdscript_utility_functions.h index 005b49c5da..c6d3718844 100644 --- a/modules/gdscript/gdscript_functions.h +++ b/modules/gdscript/gdscript_utility_functions.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* gdscript_functions.h */ +/* gdscript_utility_functions.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). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,110 +28,31 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef GDSCRIPT_FUNCTIONS_H -#define GDSCRIPT_FUNCTIONS_H +#ifndef GDSCRIPT_UTILITY_FUNCTIONS_H +#define GDSCRIPT_UTILITY_FUNCTIONS_H +#include "core/string/string_name.h" #include "core/variant/variant.h" -class GDScriptFunctions { +class GDScriptUtilityFunctions { public: - enum Function { - MATH_SIN, - MATH_COS, - MATH_TAN, - MATH_SINH, - MATH_COSH, - MATH_TANH, - MATH_ASIN, - MATH_ACOS, - MATH_ATAN, - MATH_ATAN2, - MATH_SQRT, - MATH_FMOD, - MATH_FPOSMOD, - MATH_POSMOD, - MATH_FLOOR, - MATH_CEIL, - MATH_ROUND, - MATH_ABS, - MATH_SIGN, - MATH_POW, - MATH_LOG, - MATH_EXP, - MATH_ISNAN, - MATH_ISINF, - MATH_ISEQUALAPPROX, - MATH_ISZEROAPPROX, - MATH_EASE, - MATH_STEP_DECIMALS, - MATH_STEPIFY, - MATH_LERP, - MATH_LERP_ANGLE, - MATH_INVERSE_LERP, - MATH_RANGE_LERP, - MATH_SMOOTHSTEP, - MATH_MOVE_TOWARD, - MATH_DECTIME, - MATH_RANDOMIZE, - MATH_RANDI, - MATH_RANDF, - MATH_RANDF_RANGE, - MATH_RANDI_RANGE, - MATH_SEED, - MATH_RANDSEED, - MATH_DEG2RAD, - MATH_RAD2DEG, - MATH_LINEAR2DB, - MATH_DB2LINEAR, - MATH_POLAR2CARTESIAN, - MATH_CARTESIAN2POLAR, - MATH_WRAP, - MATH_WRAPF, - LOGIC_MAX, - LOGIC_MIN, - LOGIC_CLAMP, - LOGIC_NEAREST_PO2, - OBJ_WEAKREF, - TYPE_CONVERT, - TYPE_OF, - TYPE_EXISTS, - TEXT_CHAR, - TEXT_ORD, - TEXT_STR, - TEXT_PRINT, - TEXT_PRINT_TABBED, - TEXT_PRINT_SPACED, - TEXT_PRINTERR, - TEXT_PRINTRAW, - TEXT_PRINT_DEBUG, - PUSH_ERROR, - PUSH_WARNING, - VAR_TO_STR, - STR_TO_VAR, - VAR_TO_BYTES, - BYTES_TO_VAR, - GEN_RANGE, - RESOURCE_LOAD, - INST2DICT, - DICT2INST, - VALIDATE_JSON, - PARSE_JSON, - TO_JSON, - HASH, - COLOR8, - COLORN, - PRINT_STACK, - GET_STACK, - INSTANCE_FROM_ID, - LEN, - IS_INSTANCE_VALID, - FUNC_MAX - }; + typedef void (*FunctionPtr)(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error); - static const char *get_func_name(Function p_func); - static void call(Function p_func, const Variant **p_args, int p_arg_count, Variant &r_ret, Callable::CallError &r_error); - static bool is_deterministic(Function p_func); - static MethodInfo get_info(Function p_func); + static FunctionPtr get_function(const StringName &p_function); + static bool has_function_return_value(const StringName &p_function); + static Variant::Type get_function_return_type(const StringName &p_function); + static StringName get_function_return_class(const StringName &p_function); + static Variant::Type get_function_argument_type(const StringName &p_function, int p_arg); + static int get_function_argument_count(const StringName &p_function, int p_arg); + static bool is_function_vararg(const StringName &p_function); + static bool is_function_constant(const StringName &p_function); + + static bool function_exists(const StringName &p_function); + static void get_function_list(List<StringName> *r_functions); + static MethodInfo get_function_info(const StringName &p_function); + + static void register_functions(); + static void unregister_functions(); }; -#endif // GDSCRIPT_FUNCTIONS_H +#endif // GDSCRIPT_UTILITY_FUNCTIONS_H diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index c33079c8ee..4e098d7a6d 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,7 +33,6 @@ #include "core/core_string_names.h" #include "core/os/os.h" #include "gdscript.h" -#include "gdscript_functions.h" Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_instance, GDScript *p_script, Variant &self, Variant &static_ref, Variant *p_stack, String &r_error) const { int address = p_address & ADDR_MASK; @@ -220,7 +219,9 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const &&OPCODE_CALL, \ &&OPCODE_CALL_RETURN, \ &&OPCODE_CALL_ASYNC, \ - &&OPCODE_CALL_BUILT_IN, \ + &&OPCODE_CALL_UTILITY, \ + &&OPCODE_CALL_UTILITY_VALIDATED, \ + &&OPCODE_CALL_GDSCRIPT_UTILITY, \ &&OPCODE_CALL_BUILTIN_TYPE_VALIDATED, \ &&OPCODE_CALL_SELF_BASE, \ &&OPCODE_CALL_METHOD_BIND, \ @@ -734,7 +735,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a const Variant::ValidatedKeyedSetter setter = _keyed_setters_ptr[index_setter]; bool valid; - setter(dst, index, value, valid); + setter(dst, index, value, &valid); #ifdef DEBUG_ENABLED if (!valid) { @@ -766,7 +767,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a int64_t int_index = *VariantInternal::get_int(index); bool oob; - setter(dst, int_index, value, oob); + setter(dst, int_index, value, &oob); #ifdef DEBUG_ENABLED if (oob) { @@ -831,9 +832,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #ifdef DEBUG_ENABLED // Allow better error message in cases where src and dst are the same stack position. Variant ret; - getter(src, key, &ret, valid); + getter(src, key, &ret, &valid); #else - getter(src, key, dst, valid); + getter(src, key, dst, &valid); #endif #ifdef DEBUG_ENABLED if (!valid) { @@ -866,7 +867,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a int64_t int_index = *VariantInternal::get_int(index); bool oob; - getter(src, int_index, dst, oob); + getter(src, int_index, dst, &oob); #ifdef DEBUG_ENABLED if (oob) { @@ -1288,7 +1289,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GET_INSTRUCTION_ARG(dst, argc); - constructor(*dst, (const Variant **)argptrs); + constructor(dst, (const Variant **)argptrs); ip += 3; } @@ -1745,7 +1746,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } DISPATCH_OPCODE; - OPCODE(OPCODE_CALL_BUILT_IN) { + OPCODE(OPCODE_CALL_UTILITY) { CHECK_SPACE(3 + instr_arg_count); ip += instr_arg_count; @@ -1753,22 +1754,80 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a int argc = _code_ptr[ip + 1]; GD_ERR_BREAK(argc < 0); - GDScriptFunctions::Function func = GDScriptFunctions::Function(_code_ptr[ip + 2]); + GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _global_names_count); + StringName function = _global_names_ptr[_code_ptr[ip + 2]]; + Variant **argptrs = instruction_args; GET_INSTRUCTION_ARG(dst, argc); Callable::CallError err; - GDScriptFunctions::call(func, (const Variant **)argptrs, argc, *dst, err); + Variant::call_utility_function(function, dst, (const Variant **)argptrs, argc, err); #ifdef DEBUG_ENABLED if (err.error != Callable::CallError::CALL_OK) { - String methodstr = GDScriptFunctions::get_func_name(func); + String methodstr = function; if (dst->get_type() == Variant::STRING) { - //call provided error string - err_text = "Error calling built-in function '" + methodstr + "': " + String(*dst); + // Call provided error string. + err_text = "Error calling utility function '" + methodstr + "': " + String(*dst); } else { - err_text = _get_call_error(err, "built-in function '" + methodstr + "'", (const Variant **)argptrs); + err_text = _get_call_error(err, "utility function '" + methodstr + "'", (const Variant **)argptrs); + } + OPCODE_BREAK; + } +#endif + ip += 3; + } + DISPATCH_OPCODE; + + OPCODE(OPCODE_CALL_UTILITY_VALIDATED) { + CHECK_SPACE(3 + instr_arg_count); + + ip += instr_arg_count; + + int argc = _code_ptr[ip + 1]; + GD_ERR_BREAK(argc < 0); + + GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _utilities_count); + Variant::ValidatedUtilityFunction function = _utilities_ptr[_code_ptr[ip + 2]]; + + Variant **argptrs = instruction_args; + + GET_INSTRUCTION_ARG(dst, argc); + + function(dst, (const Variant **)argptrs, argc); + + ip += 3; + } + DISPATCH_OPCODE; + + OPCODE(OPCODE_CALL_GDSCRIPT_UTILITY) { + CHECK_SPACE(3 + instr_arg_count); + + ip += instr_arg_count; + + int argc = _code_ptr[ip + 1]; + GD_ERR_BREAK(argc < 0); + + GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _gds_utilities_count); + GDScriptUtilityFunctions::FunctionPtr function = _gds_utilities_ptr[_code_ptr[ip + 2]]; + + Variant **argptrs = instruction_args; + + GET_INSTRUCTION_ARG(dst, argc); + + Callable::CallError err; + function(dst, (const Variant **)argptrs, argc, err); + +#ifdef DEBUG_ENABLED + if (err.error != Callable::CallError::CALL_OK) { + // TODO: Add this information in debug. + String methodstr = "<unkown function>"; + if (dst->get_type() == Variant::STRING) { + // Call provided error string. + err_text = "Error calling GDScript utility function '" + methodstr + "': " + String(*dst); + } else { + err_text = _get_call_error(err, "GDScript utility function '" + methodstr + "'", (const Variant **)argptrs); } OPCODE_BREAK; } @@ -2219,7 +2278,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a VariantInternal::initialize(counter, Variant::INT); *VariantInternal::get_int(counter) = 0; - if (!str->empty()) { + if (!str->is_empty()) { GET_INSTRUCTION_ARG(iterator, 2); VariantInternal::initialize(iterator, Variant::STRING); *VariantInternal::get_string(iterator) = str->substr(0, 1); @@ -2243,10 +2302,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a Dictionary *dict = VariantInternal::get_dictionary(container); const Variant *next = dict->next(nullptr); - *counter = *next; - if (!dict->empty()) { + if (!dict->is_empty()) { GET_INSTRUCTION_ARG(iterator, 2); + *counter = *next; *iterator = *next; // Skip regular iterate. @@ -2271,7 +2330,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a VariantInternal::initialize(counter, Variant::INT); *VariantInternal::get_int(counter) = 0; - if (!array->empty()) { + if (!array->is_empty()) { GET_INSTRUCTION_ARG(iterator, 2); *iterator = array->get(0); @@ -2294,7 +2353,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a Vector<m_elem_type> *array = VariantInternal::m_get_func(container); \ VariantInternal::initialize(counter, Variant::INT); \ *VariantInternal::get_int(counter) = 0; \ - if (!array->empty()) { \ + if (!array->is_empty()) { \ GET_INSTRUCTION_ARG(iterator, 2); \ VariantInternal::initialize(iterator, Variant::m_var_ret_type); \ m_ret_type *it = VariantInternal::m_ret_get_func(iterator); \ @@ -2718,7 +2777,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GET_INSTRUCTION_ARG(message, 1); message_str = *message; } - if (message_str.empty()) { + if (message_str.is_empty()) { err_text = "Assertion failed."; } else { err_text = "Assertion failed: " + message_str; diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp index 56704d3e0a..ad41b60a4e 100644 --- a/modules/gdscript/gdscript_warning.cpp +++ b/modules/gdscript/gdscript_warning.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h index e0857703d8..4b295b5eb8 100644 --- a/modules/gdscript/gdscript_warning.h +++ b/modules/gdscript/gdscript_warning.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp index 668dfd4835..e63b6ab20e 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.cpp +++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -147,7 +147,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p r_symbol.script_path = path; r_symbol.children.clear(); r_symbol.name = p_class->identifier != nullptr ? String(p_class->identifier->name) : String(); - if (r_symbol.name.empty()) { + if (r_symbol.name.is_empty()) { r_symbol.name = path.get_file(); } r_symbol.kind = lsp::SymbolKind::Class; @@ -215,9 +215,9 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p String value_text; if (default_value.get_type() == Variant::OBJECT) { RES res = default_value; - if (res.is_valid() && !res->get_path().empty()) { + if (res.is_valid() && !res->get_path().is_empty()) { value_text = "preload(\"" + res->get_path() + "\")"; - if (symbol.documentation.empty()) { + if (symbol.documentation.is_empty()) { if (Map<String, ExtendGDScriptParser *>::Element *S = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(res->get_path())) { symbol.documentation = S->get()->class_symbol.documentation; } @@ -228,7 +228,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p } else { value_text = JSON::print(default_value); } - if (!value_text.empty()) { + if (!value_text.is_empty()) { symbol.detail += " = " + value_text; } @@ -453,7 +453,7 @@ String ExtendGDScriptParser::get_text_for_lookup_symbol(const lsp::Position &p_c String line = lines[i]; String first_part = line.substr(0, p_cursor.character); String last_part = line.substr(p_cursor.character + 1, lines[i].length()); - if (!p_symbol.empty()) { + if (!p_symbol.is_empty()) { String left_cursor_text; for (int c = p_cursor.character - 1; c >= 0; c--) { left_cursor_text = line.substr(c, p_cursor.character - c); @@ -589,7 +589,7 @@ const lsp::DocumentSymbol *ExtendGDScriptParser::get_symbol_defined_at_line(int } const lsp::DocumentSymbol *ExtendGDScriptParser::get_member_symbol(const String &p_name, const String &p_subclass) const { - if (p_subclass.empty()) { + if (p_subclass.is_empty()) { const lsp::DocumentSymbol *const *ptr = members.getptr(p_name); if (ptr) { return *ptr; @@ -611,7 +611,7 @@ const List<lsp::DocumentLink> &ExtendGDScriptParser::get_document_links() const } const Array &ExtendGDScriptParser::get_member_completions() { - if (member_completions.empty()) { + if (member_completions.is_empty()) { const String *name = members.next(nullptr); while (name) { const lsp::DocumentSymbol *symbol = members.get(*name); @@ -723,8 +723,8 @@ Dictionary ExtendGDScriptParser::dump_class_api(const GDScriptParser::ClassNode } break; case ClassNode::Member::ENUM: { Dictionary enum_dict; - for (int j = 0; j < m.m_enum->values.size(); i++) { - enum_dict[m.m_enum->values[i].identifier->name] = m.m_enum->values[i].value; + for (int j = 0; j < m.m_enum->values.size(); j++) { + enum_dict[m.m_enum->values[j].identifier->name] = m.m_enum->values[j].value; } Dictionary api; diff --git a/modules/gdscript/language_server/gdscript_extend_parser.h b/modules/gdscript/language_server/gdscript_extend_parser.h index bb02d3dc99..28b9b3c82a 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.h +++ b/modules/gdscript/language_server/gdscript_extend_parser.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp index 6ddb0d149e..5e3d6213d3 100644 --- a/modules/gdscript/language_server/gdscript_language_protocol.cpp +++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,6 +33,7 @@ #include "core/config/project_settings.h" #include "core/io/json.h" #include "core/os/copymem.h" +#include "editor/doc_tools.h" #include "editor/editor_log.h" #include "editor/editor_node.h" @@ -95,7 +96,7 @@ Error GDScriptLanguageProtocol::LSPeer::handle_data() { // Response String output = GDScriptLanguageProtocol::get_singleton()->process_message(msg); - if (!output.empty()) { + if (!output.is_empty()) { res_queue.push_back(output.utf8()); } } @@ -104,7 +105,7 @@ Error GDScriptLanguageProtocol::LSPeer::handle_data() { Error GDScriptLanguageProtocol::LSPeer::send_data() { int sent = 0; - if (!res_queue.empty()) { + if (!res_queue.is_empty()) { CharString c_res = res_queue[0]; if (res_sent < c_res.size()) { Error err = connection->put_partial_data((const uint8_t *)c_res.get_data() + res_sent, c_res.size() - res_sent - 1, sent); @@ -140,7 +141,7 @@ void GDScriptLanguageProtocol::on_client_disconnected(const int &p_client_id) { String GDScriptLanguageProtocol::process_message(const String &p_text) { String ret = process_string(p_text); - if (ret.empty()) { + if (ret.is_empty()) { return ret; } else { return format_output(ret); @@ -212,7 +213,7 @@ Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) { void GDScriptLanguageProtocol::initialized(const Variant &p_params) { lsp::GodotCapabilities capabilities; - DocData *doc = EditorHelp::get_doc_data(); + DocTools *doc = EditorHelp::get_doc_data(); for (Map<String, DocData::ClassDoc>::Element *E = doc->class_list.front(); E; E = E->next()) { lsp::GodotNativeClassInfo gdclass; gdclass.name = E->get().name; diff --git a/modules/gdscript/language_server/gdscript_language_protocol.h b/modules/gdscript/language_server/gdscript_language_protocol.h index cf5242e8c5..8b08ae0655 100644 --- a/modules/gdscript/language_server/gdscript_language_protocol.h +++ b/modules/gdscript/language_server/gdscript_language_protocol.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdscript/language_server/gdscript_language_server.cpp b/modules/gdscript/language_server/gdscript_language_server.cpp index 3387d262f8..aac9cb7fd7 100644 --- a/modules/gdscript/language_server/gdscript_language_server.cpp +++ b/modules/gdscript/language_server/gdscript_language_server.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdscript/language_server/gdscript_language_server.h b/modules/gdscript/language_server/gdscript_language_server.h index 228d29bf42..218f42199e 100644 --- a/modules/gdscript/language_server/gdscript_language_server.h +++ b/modules/gdscript/language_server/gdscript_language_server.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp index c6fe3169dc..9f2373bf56 100644 --- a/modules/gdscript/language_server/gdscript_text_document.cpp +++ b/modules/gdscript/language_server/gdscript_text_document.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -147,7 +147,7 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) { List<ScriptCodeCompletionOption> options; GDScriptLanguageProtocol::get_singleton()->get_workspace()->completion(params, &options); - if (!options.empty()) { + if (!options.is_empty()) { int i = 0; arr.resize(options.size()); @@ -257,7 +257,7 @@ Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) { if ((item.kind == lsp::CompletionItemKind::Method || item.kind == lsp::CompletionItemKind::Function) && !item.label.ends_with("):")) { item.insertText = item.label + "("; - if (symbol && symbol->children.empty()) { + if (symbol && symbol->children.is_empty()) { item.insertText += ")"; } } else if (item.kind == lsp::CompletionItemKind::Event) { @@ -341,7 +341,7 @@ Variant GDScriptTextDocument::declaration(const Dictionary &p_params) { params.load(p_params); List<const lsp::DocumentSymbol *> symbols; Array arr = this->find_symbols(params, symbols); - if (arr.empty() && !symbols.empty() && !symbols.front()->get()->native_class.empty()) { // Find a native symbol + if (arr.is_empty() && !symbols.is_empty() && !symbols.front()->get()->native_class.is_empty()) { // Find a native symbol const lsp::DocumentSymbol *symbol = symbols.front()->get(); if (GDScriptLanguageProtocol::get_singleton()->is_goto_native_symbols_enabled()) { String id; @@ -425,7 +425,7 @@ Array GDScriptTextDocument::find_symbols(const lsp::TextDocumentPositionParams & GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_related_symbols(p_location, list); for (List<const lsp::DocumentSymbol *>::Element *E = list.front(); E; E = E->next()) { if (const lsp::DocumentSymbol *s = E->get()) { - if (!s->uri.empty()) { + if (!s->uri.is_empty()) { lsp::Location location; location.uri = s->uri; location.range = s->range; diff --git a/modules/gdscript/language_server/gdscript_text_document.h b/modules/gdscript/language_server/gdscript_text_document.h index 2a5755bec6..792e601bc1 100644 --- a/modules/gdscript/language_server/gdscript_text_document.h +++ b/modules/gdscript/language_server/gdscript_text_document.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp index f6643d07f9..7b502f079b 100644 --- a/modules/gdscript/language_server/gdscript_workspace.cpp +++ b/modules/gdscript/language_server/gdscript_workspace.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -34,6 +34,7 @@ #include "../gdscript_parser.h" #include "core/config/project_settings.h" #include "core/object/script_language.h" +#include "editor/doc_tools.h" #include "editor/editor_file_system.h" #include "editor/editor_help.h" #include "editor/editor_node.h" @@ -79,7 +80,7 @@ const lsp::DocumentSymbol *GDScriptWorkspace::get_native_symbol(const String &p_ if (const Map<StringName, lsp::DocumentSymbol>::Element *E = native_symbols.find(class_name)) { const lsp::DocumentSymbol &class_symbol = E->value(); - if (p_member.empty()) { + if (p_member.is_empty()) { return &class_symbol; } else { for (int i = 0; i < class_symbol.children.size(); i++) { @@ -170,7 +171,7 @@ ExtendGDScriptParser *GDScriptWorkspace::get_parse_result(const String &p_path) Array GDScriptWorkspace::symbol(const Dictionary &p_params) { String query = p_params["query"]; Array arr; - if (!query.empty()) { + if (!query.is_empty()) { for (Map<String, ExtendGDScriptParser *>::Element *E = scripts.front(); E; E = E->next()) { Vector<lsp::DocumentedSymbolInformation> script_symbols; E->get()->get_symbols().symbol_tree_as_list(E->key(), script_symbols); @@ -189,7 +190,7 @@ Error GDScriptWorkspace::initialize() { return OK; } - DocData *doc = EditorHelp::get_doc_data(); + DocTools *doc = EditorHelp::get_doc_data(); for (Map<String, DocData::ClassDoc>::Element *E = doc->class_list.front(); E; E = E->next()) { const DocData::ClassDoc &class_data = E->value(); lsp::DocumentSymbol class_symbol; @@ -198,7 +199,7 @@ Error GDScriptWorkspace::initialize() { class_symbol.native_class = class_name; class_symbol.kind = lsp::SymbolKind::Class; class_symbol.detail = String("<Native> class ") + class_name; - if (!class_data.inherits.empty()) { + if (!class_data.inherits.is_empty()) { class_symbol.detail += " extends " + class_data.inherits; } class_symbol.documentation = class_data.brief_description + "\n" + class_data.description; @@ -262,7 +263,7 @@ Error GDScriptWorkspace::initialize() { symbol_arg.kind = lsp::SymbolKind::Variable; symbol_arg.detail = arg.type; - if (!arg_default_value_started && !arg.default_value.empty()) { + if (!arg_default_value_started && !arg.default_value.is_empty()) { arg_default_value_started = true; } String arg_str = arg.name + ": " + arg.type; @@ -277,11 +278,11 @@ Error GDScriptWorkspace::initialize() { symbol.children.push_back(symbol_arg); } if (data.qualifiers.find("vararg") != -1) { - params += params.empty() ? "..." : ", ..."; + params += params.is_empty() ? "..." : ", ..."; } String return_type = data.return_type; - if (return_type.empty()) { + if (return_type.is_empty()) { return_type = "void"; } symbol.detail = "func " + class_name + "." + data.name + "(" + params + ") -> " + return_type; @@ -447,13 +448,13 @@ const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocu } lsp::Position pos = p_doc_pos.position; - if (symbol_identifier.empty()) { + if (symbol_identifier.is_empty()) { Vector2i offset; symbol_identifier = parser->get_identifier_under_position(p_doc_pos.position, offset); pos.character += offset.y; } - if (!symbol_identifier.empty()) { + if (!symbol_identifier.is_empty()) { if (ScriptServer::is_global_class(symbol_identifier)) { String class_path = ScriptServer::get_global_class_path(symbol_identifier); symbol = get_script_symbol(class_path); @@ -473,7 +474,7 @@ const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocu } else { String member = ret.class_member; - if (member.empty() && symbol_identifier != ret.class_name) { + if (member.is_empty() && symbol_identifier != ret.class_name) { member = symbol_identifier; } symbol = get_native_symbol(ret.class_name, member); @@ -528,7 +529,7 @@ void GDScriptWorkspace::resolve_related_symbols(const lsp::TextDocumentPositionP const lsp::DocumentSymbol *GDScriptWorkspace::resolve_native_symbol(const lsp::NativeSymbolInspectParams &p_params) { if (Map<StringName, lsp::DocumentSymbol>::Element *E = native_symbols.find(p_params.native_class)) { const lsp::DocumentSymbol &symbol = E->get(); - if (p_params.symbol_name.empty() || p_params.symbol_name == symbol.name) { + if (p_params.symbol_name.is_empty() || p_params.symbol_name == symbol.name) { return &symbol; } diff --git a/modules/gdscript/language_server/gdscript_workspace.h b/modules/gdscript/language_server/gdscript_workspace.h index fc244c6357..7fd8bfcf20 100644 --- a/modules/gdscript/language_server/gdscript_workspace.h +++ b/modules/gdscript/language_server/gdscript_workspace.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp index 288fd41c87..6a913edbbf 100644 --- a/modules/gdscript/language_server/lsp.hpp +++ b/modules/gdscript/language_server/lsp.hpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,9 +31,9 @@ #ifndef GODOT_LSP_H #define GODOT_LSP_H +#include "core/doc_data.h" #include "core/object/class_db.h" #include "core/templates/list.h" -#include "editor/doc_data.h" namespace lsp { @@ -687,7 +687,7 @@ struct Diagnostic { dict["severity"] = severity; dict["message"] = message; dict["source"] = source; - if (!relatedInformation.empty()) { + if (!relatedInformation.is_empty()) { Array arr; arr.resize(relatedInformation.size()); for (int i = 0; i < relatedInformation.size(); i++) { @@ -1191,7 +1191,7 @@ struct DocumentSymbol { void symbol_tree_as_list(const String &p_uri, Vector<DocumentedSymbolInformation> &r_list, const String &p_container = "", bool p_join_name = false) const { DocumentedSymbolInformation si; - if (p_join_name && !p_container.empty()) { + if (p_join_name && !p_container.is_empty()) { si.name = p_container + ">" + name; } else { si.name = name; diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp index 6c2af66c65..e90475a60e 100644 --- a/modules/gdscript/register_types.cpp +++ b/modules/gdscript/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -38,6 +38,7 @@ #include "gdscript_analyzer.h" #include "gdscript_cache.h" #include "gdscript_tokenizer.h" +#include "gdscript_utility_functions.h" #ifdef TESTS_ENABLED #include "tests/test_gdscript.h" @@ -130,6 +131,8 @@ void register_gdscript_types() { gdscript_translation_parser_plugin.instance(); EditorTranslationParser::get_singleton()->add_parser(gdscript_translation_parser_plugin, EditorTranslationParser::STANDARD); #endif // TOOLS_ENABLED + + GDScriptUtilityFunctions::register_functions(); } void unregister_gdscript_types() { @@ -156,6 +159,7 @@ void unregister_gdscript_types() { GDScriptParser::cleanup(); GDScriptAnalyzer::cleanup(); + GDScriptUtilityFunctions::unregister_functions(); } #ifdef TESTS_ENABLED diff --git a/modules/gdscript/register_types.h b/modules/gdscript/register_types.h index 18e57c1211..ce1c03d1d0 100644 --- a/modules/gdscript/register_types.h +++ b/modules/gdscript/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdscript/tests/test_gdscript.cpp b/modules/gdscript/tests/test_gdscript.cpp index 643c2f10a2..898ac653f5 100644 --- a/modules/gdscript/tests/test_gdscript.cpp +++ b/modules/gdscript/tests/test_gdscript.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -239,7 +239,7 @@ void init_autoloads() { void test(TestType p_type) { List<String> cmdlargs = OS::get_singleton()->get_cmdline_args(); - if (cmdlargs.empty()) { + if (cmdlargs.is_empty()) { return; } diff --git a/modules/gdscript/tests/test_gdscript.h b/modules/gdscript/tests/test_gdscript.h index 6182629802..bbda46cdad 100644 --- a/modules/gdscript/tests/test_gdscript.h +++ b/modules/gdscript/tests/test_gdscript.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/glslang/SCsub b/modules/glslang/SCsub index 58c033c75d..182272ffc7 100644 --- a/modules/glslang/SCsub +++ b/modules/glslang/SCsub @@ -6,6 +6,9 @@ Import("env_modules") env_glslang = env_modules.Clone() # Thirdparty source files + +thirdparty_obj = [] + if env["builtin_glslang"]: thirdparty_dir = "#thirdparty/glslang/" thirdparty_sources = [ @@ -70,7 +73,16 @@ if env["builtin_glslang"]: env_thirdparty = env_glslang.Clone() env_thirdparty.disable_warnings() - env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) + env.modules_sources += thirdparty_obj + + +# Godot source files + +module_obj = [] + +env_glslang.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj -# Godot's own source files -env_glslang.add_source_files(env.modules_sources, "*.cpp") +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/glslang/register_types.cpp b/modules/glslang/register_types.cpp index 3238e0108e..a7ae3e6bab 100644 --- a/modules/glslang/register_types.cpp +++ b/modules/glslang/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/glslang/register_types.h b/modules/glslang/register_types.h index 2437e2b27a..a1264b77c9 100644 --- a/modules/glslang/register_types.h +++ b/modules/glslang/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gltf/SCsub b/modules/gltf/SCsub new file mode 100644 index 0000000000..5d03ee8361 --- /dev/null +++ b/modules/gltf/SCsub @@ -0,0 +1,10 @@ +#!/usr/bin/env python + +Import("env") +Import("env_modules") + +env_gltf = env_modules.Clone() +env_gltf.Prepend(CPPPATH=["."]) + +# Godot's own source files +env_gltf.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/assimp/config.py b/modules/gltf/config.py index 53b8f2f2e3..1505a456d7 100644 --- a/modules/assimp/config.py +++ b/modules/gltf/config.py @@ -1,5 +1,5 @@ def can_build(env, platform): - return env["tools"] + return env["tools"] and not env["disable_3d"] def configure(env): diff --git a/modules/gltf/editor_scene_exporter_gltf_plugin.cpp b/modules/gltf/editor_scene_exporter_gltf_plugin.cpp new file mode 100644 index 0000000000..4cdaccde6f --- /dev/null +++ b/modules/gltf/editor_scene_exporter_gltf_plugin.cpp @@ -0,0 +1,90 @@ +/*************************************************************************/ +/* editor_scene_exporter_gltf_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "editor_scene_exporter_gltf_plugin.h" +#include "core/config/project_settings.h" +#include "core/object/object.h" +#include "core/templates/vector.h" +#include "editor/editor_file_system.h" +#include "scene/3d/mesh_instance_3d.h" +#include "scene/gui/check_box.h" +#include "scene/main/node.h" + +#include "editor/editor_node.h" + +String SceneExporterGLTFPlugin::get_name() const { + return "ConvertGLTF2"; +} + +bool SceneExporterGLTFPlugin::has_main_screen() const { + return false; +} + +SceneExporterGLTFPlugin::SceneExporterGLTFPlugin(EditorNode *p_node) { + editor = p_node; + convert_gltf2.instance(); + file_export_lib = memnew(EditorFileDialog); + editor->get_gui_base()->add_child(file_export_lib); + file_export_lib->connect("file_selected", callable_mp(this, &SceneExporterGLTFPlugin::_gltf2_dialog_action)); + file_export_lib->set_title(TTR("Export Library")); + file_export_lib->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE); + file_export_lib->set_access(EditorFileDialog::ACCESS_FILESYSTEM); + file_export_lib->clear_filters(); + file_export_lib->add_filter("*.glb"); + file_export_lib->add_filter("*.gltf"); + file_export_lib->set_title(TTR("Export Mesh GLTF2")); + String gltf_scene_name = TTR("Export GLTF..."); + add_tool_menu_item(gltf_scene_name, callable_mp(this, &SceneExporterGLTFPlugin::convert_scene_to_gltf2)); +} + +void SceneExporterGLTFPlugin::_gltf2_dialog_action(String p_file) { + Node *root = editor->get_tree()->get_edited_scene_root(); + if (!root) { + editor->show_accept(TTR("This operation can't be done without a scene."), TTR("OK")); + return; + } + List<String> deps; + convert_gltf2->save_scene(root, p_file, p_file, 0, 1000.0f, &deps); + EditorFileSystem::get_singleton()->scan_changes(); +} + +void SceneExporterGLTFPlugin::convert_scene_to_gltf2() { + Node *root = editor->get_tree()->get_edited_scene_root(); + if (!root) { + editor->show_accept(TTR("This operation can't be done without a scene."), TTR("OK")); + return; + } + String filename = String(root->get_filename().get_file().get_basename()); + if (filename.is_empty()) { + filename = root->get_name(); + } + file_export_lib->set_current_file(filename + String(".gltf")); + file_export_lib->popup_centered_ratio(); +} diff --git a/modules/gltf/editor_scene_exporter_gltf_plugin.h b/modules/gltf/editor_scene_exporter_gltf_plugin.h new file mode 100644 index 0000000000..d952894c16 --- /dev/null +++ b/modules/gltf/editor_scene_exporter_gltf_plugin.h @@ -0,0 +1,52 @@ +/*************************************************************************/ +/* editor_scene_exporter_gltf_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef EDITOR_SCENE_EXPORTER_GLTF_PLUGIN_H +#define EDITOR_SCENE_EXPORTER_GLTF_PLUGIN_H + +#include "editor/editor_plugin.h" +#include "editor_scene_importer_gltf.h" + +class SceneExporterGLTFPlugin : public EditorPlugin { + GDCLASS(SceneExporterGLTFPlugin, EditorPlugin); + + Ref<PackedSceneGLTF> convert_gltf2; + EditorNode *editor = nullptr; + EditorFileDialog *file_export_lib = nullptr; + void _gltf2_dialog_action(String p_file); + void convert_scene_to_gltf2(); + +public: + virtual String get_name() const override; + bool has_main_screen() const override; + SceneExporterGLTFPlugin(class EditorNode *p_node); +}; + +#endif // EDITOR_SCENE_EXPORTER_GLTF_PLUGIN_H diff --git a/modules/gltf/editor_scene_importer_gltf.cpp b/modules/gltf/editor_scene_importer_gltf.cpp new file mode 100644 index 0000000000..6ea722a216 --- /dev/null +++ b/modules/gltf/editor_scene_importer_gltf.cpp @@ -0,0 +1,180 @@ +/*************************************************************************/ +/* editor_scene_importer_gltf.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "core/crypto/crypto_core.h" +#include "core/io/json.h" +#include "core/math/disjoint_set.h" +#include "core/math/math_defs.h" +#include "core/os/file_access.h" +#include "core/os/os.h" +#include "editor/import/resource_importer_scene.h" +#include "modules/gltf/gltf_state.h" +#include "modules/regex/regex.h" +#include "scene/3d/bone_attachment_3d.h" +#include "scene/3d/camera_3d.h" +#include "scene/3d/mesh_instance_3d.h" +#include "scene/animation/animation_player.h" +#include "scene/resources/packed_scene.h" +#include "scene/resources/surface_tool.h" + +#include "modules/gltf/editor_scene_importer_gltf.h" + +uint32_t EditorSceneImporterGLTF::get_import_flags() const { + return ImportFlags::IMPORT_SCENE | ImportFlags::IMPORT_ANIMATION; +} + +void EditorSceneImporterGLTF::get_extensions(List<String> *r_extensions) const { + r_extensions->push_back("gltf"); + r_extensions->push_back("glb"); +} + +Node *EditorSceneImporterGLTF::import_scene(const String &p_path, + uint32_t p_flags, int p_bake_fps, + List<String> *r_missing_deps, + Error *r_err) { + Ref<PackedSceneGLTF> importer; + importer.instance(); + return importer->import_scene(p_path, p_flags, p_bake_fps, r_missing_deps, r_err, Ref<GLTFState>()); +} + +Ref<Animation> EditorSceneImporterGLTF::import_animation(const String &p_path, + uint32_t p_flags, + int p_bake_fps) { + return Ref<Animation>(); +} + +void PackedSceneGLTF::_bind_methods() { + ClassDB::bind_method( + D_METHOD("export_gltf", "node", "path", "flags", "bake_fps"), + &PackedSceneGLTF::export_gltf, DEFVAL(0), DEFVAL(1000.0f)); + ClassDB::bind_method(D_METHOD("pack_gltf", "path", "flags", "bake_fps", "state"), + &PackedSceneGLTF::pack_gltf, DEFVAL(0), DEFVAL(1000.0f), DEFVAL(Ref<GLTFState>())); + ClassDB::bind_method(D_METHOD("import_gltf_scene", "path", "flags", "bake_fps", "state"), + &PackedSceneGLTF::import_gltf_scene, DEFVAL(0), DEFVAL(1000.0f), DEFVAL(Ref<GLTFState>())); +} +Node *PackedSceneGLTF::import_gltf_scene(const String &p_path, uint32_t p_flags, float p_bake_fps, Ref<GLTFState> r_state) { + Error err = FAILED; + List<String> deps; + return import_scene(p_path, p_flags, p_bake_fps, &deps, &err, r_state); +} + +Node *PackedSceneGLTF::import_scene(const String &p_path, uint32_t p_flags, + int p_bake_fps, + List<String> *r_missing_deps, + Error *r_err, + Ref<GLTFState> r_state) { + if (r_state == Ref<GLTFState>()) { + r_state.instance(); + } + r_state->use_named_skin_binds = + p_flags & EditorSceneImporter::IMPORT_USE_NAMED_SKIN_BINDS; + + Ref<GLTFDocument> gltf_document; + gltf_document.instance(); + Error err = gltf_document->parse(r_state, p_path); + *r_err = err; + ERR_FAIL_COND_V(err != Error::OK, nullptr); + + Node3D *root = memnew(Node3D); + for (int32_t root_i = 0; root_i < r_state->root_nodes.size(); root_i++) { + gltf_document->_generate_scene_node(r_state, root, root, r_state->root_nodes[root_i]); + } + gltf_document->_process_mesh_instances(r_state, root); + if (r_state->animations.size()) { + AnimationPlayer *ap = memnew(AnimationPlayer); + root->add_child(ap); + ap->set_owner(root); + for (int i = 0; i < r_state->animations.size(); i++) { + gltf_document->_import_animation(r_state, ap, i, p_bake_fps); + } + } + + return cast_to<Node3D>(root); +} + +void PackedSceneGLTF::pack_gltf(String p_path, int32_t p_flags, + real_t p_bake_fps, Ref<GLTFState> r_state) { + Error err = FAILED; + List<String> deps; + Node *root = import_scene(p_path, p_flags, p_bake_fps, &deps, &err, r_state); + ERR_FAIL_COND(err != OK); + pack(root); +} + +void PackedSceneGLTF::save_scene(Node *p_node, const String &p_path, + const String &p_src_path, uint32_t p_flags, + int p_bake_fps, List<String> *r_missing_deps, + Error *r_err) { + Error err = FAILED; + if (r_err) { + *r_err = err; + } + Ref<GLTFDocument> gltf_document; + gltf_document.instance(); + Ref<GLTFState> state; + state.instance(); + err = gltf_document->serialize(state, p_node, p_path); + if (r_err) { + *r_err = err; + } +} + +void PackedSceneGLTF::_build_parent_hierachy(Ref<GLTFState> state) { + // build the hierarchy + for (GLTFNodeIndex node_i = 0; node_i < state->nodes.size(); node_i++) { + for (int j = 0; j < state->nodes[node_i]->children.size(); j++) { + GLTFNodeIndex child_i = state->nodes[node_i]->children[j]; + ERR_FAIL_INDEX(child_i, state->nodes.size()); + if (state->nodes.write[child_i]->parent != -1) { + continue; + } + state->nodes.write[child_i]->parent = node_i; + } + } +} + +Error PackedSceneGLTF::export_gltf(Node *p_root, String p_path, + int32_t p_flags, + real_t p_bake_fps) { + ERR_FAIL_COND_V(!p_root, FAILED); + List<String> deps; + Error err; + String path = p_path; + int32_t flags = p_flags; + real_t baked_fps = p_bake_fps; + Ref<PackedSceneGLTF> exporter; + exporter.instance(); + exporter->save_scene(p_root, path, "", flags, baked_fps, &deps, &err); + int32_t error_code = err; + if (error_code != 0) { + return Error(error_code); + } + return OK; +} diff --git a/modules/gltf/editor_scene_importer_gltf.h b/modules/gltf/editor_scene_importer_gltf.h new file mode 100644 index 0000000000..db961e591d --- /dev/null +++ b/modules/gltf/editor_scene_importer_gltf.h @@ -0,0 +1,96 @@ +/*************************************************************************/ +/* editor_scene_importer_gltf.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef EDITOR_SCENE_IMPORTER_GLTF_H +#define EDITOR_SCENE_IMPORTER_GLTF_H + +#include "core/config/project_settings.h" +#include "core/io/json.h" +#include "core/object/object.h" +#include "core/templates/vector.h" +#include "editor/import/resource_importer_scene.h" +#include "modules/csg/csg_shape.h" +#include "modules/gridmap/grid_map.h" +#include "scene/3d/mesh_instance_3d.h" +#include "scene/3d/multimesh_instance_3d.h" +#include "scene/3d/node_3d.h" +#include "scene/3d/skeleton_3d.h" +#include "scene/animation/animation_player.h" +#include "scene/gui/check_box.h" +#include "scene/main/node.h" +#include "scene/resources/packed_scene.h" +#include "scene/resources/surface_tool.h" + +#include "gltf_document.h" +#include "gltf_state.h" + +class AnimationPlayer; +class BoneAttachment; +class EditorSceneImporterMeshNode3D; + +#ifdef TOOLS_ENABLED +class EditorSceneImporterGLTF : public EditorSceneImporter { + GDCLASS(EditorSceneImporterGLTF, EditorSceneImporter); + +public: + virtual uint32_t get_import_flags() const override; + virtual void get_extensions(List<String> *r_extensions) const override; + virtual Node *import_scene(const String &p_path, uint32_t p_flags, + int p_bake_fps, + List<String> *r_missing_deps = NULL, + Error *r_err = NULL) override; + virtual Ref<Animation> import_animation(const String &p_path, + uint32_t p_flags, int p_bake_fps) override; +}; +#endif + +class PackedSceneGLTF : public PackedScene { + GDCLASS(PackedSceneGLTF, PackedScene); + +protected: + static void _bind_methods(); + +public: + virtual void save_scene(Node *p_node, const String &p_path, const String &p_src_path, + uint32_t p_flags, int p_bake_fps, + List<String> *r_missing_deps, Error *r_err = NULL); + virtual void _build_parent_hierachy(Ref<GLTFState> state); + virtual Error export_gltf(Node *p_root, String p_path, int32_t p_flags = 0, + real_t p_bake_fps = 1000.0f); + virtual Node *import_scene(const String &p_path, uint32_t p_flags, + int p_bake_fps, + List<String> *r_missing_deps, + Error *r_err, + Ref<GLTFState> r_state); + virtual Node *import_gltf_scene(const String &p_path, uint32_t p_flags, float p_bake_fps, Ref<GLTFState> r_state = Ref<GLTFState>()); + virtual void pack_gltf(String p_path, int32_t p_flags = 0, + real_t p_bake_fps = 1000.0f, Ref<GLTFState> r_state = Ref<GLTFState>()); +}; +#endif // EDITOR_SCENE_IMPORTER_GLTF_H diff --git a/modules/gltf/gltf_accessor.cpp b/modules/gltf/gltf_accessor.cpp new file mode 100644 index 0000000000..daeb084916 --- /dev/null +++ b/modules/gltf/gltf_accessor.cpp @@ -0,0 +1,189 @@ +/*************************************************************************/ +/* gltf_accessor.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "gltf_accessor.h" + +void GLTFAccessor::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_buffer_view"), &GLTFAccessor::get_buffer_view); + ClassDB::bind_method(D_METHOD("set_buffer_view", "buffer_view"), &GLTFAccessor::set_buffer_view); + ClassDB::bind_method(D_METHOD("get_byte_offset"), &GLTFAccessor::get_byte_offset); + ClassDB::bind_method(D_METHOD("set_byte_offset", "byte_offset"), &GLTFAccessor::set_byte_offset); + ClassDB::bind_method(D_METHOD("get_component_type"), &GLTFAccessor::get_component_type); + ClassDB::bind_method(D_METHOD("set_component_type", "component_type"), &GLTFAccessor::set_component_type); + ClassDB::bind_method(D_METHOD("get_normalized"), &GLTFAccessor::get_normalized); + ClassDB::bind_method(D_METHOD("set_normalized", "normalized"), &GLTFAccessor::set_normalized); + ClassDB::bind_method(D_METHOD("get_count"), &GLTFAccessor::get_count); + ClassDB::bind_method(D_METHOD("set_count", "count"), &GLTFAccessor::set_count); + ClassDB::bind_method(D_METHOD("get_type"), &GLTFAccessor::get_type); + ClassDB::bind_method(D_METHOD("set_type", "type"), &GLTFAccessor::set_type); + ClassDB::bind_method(D_METHOD("get_min"), &GLTFAccessor::get_min); + ClassDB::bind_method(D_METHOD("set_min", "min"), &GLTFAccessor::set_min); + ClassDB::bind_method(D_METHOD("get_max"), &GLTFAccessor::get_max); + ClassDB::bind_method(D_METHOD("set_max", "max"), &GLTFAccessor::set_max); + ClassDB::bind_method(D_METHOD("get_sparse_count"), &GLTFAccessor::get_sparse_count); + ClassDB::bind_method(D_METHOD("set_sparse_count", "sparse_count"), &GLTFAccessor::set_sparse_count); + ClassDB::bind_method(D_METHOD("get_sparse_indices_buffer_view"), &GLTFAccessor::get_sparse_indices_buffer_view); + ClassDB::bind_method(D_METHOD("set_sparse_indices_buffer_view", "sparse_indices_buffer_view"), &GLTFAccessor::set_sparse_indices_buffer_view); + ClassDB::bind_method(D_METHOD("get_sparse_indices_byte_offset"), &GLTFAccessor::get_sparse_indices_byte_offset); + ClassDB::bind_method(D_METHOD("set_sparse_indices_byte_offset", "sparse_indices_byte_offset"), &GLTFAccessor::set_sparse_indices_byte_offset); + ClassDB::bind_method(D_METHOD("get_sparse_indices_component_type"), &GLTFAccessor::get_sparse_indices_component_type); + ClassDB::bind_method(D_METHOD("set_sparse_indices_component_type", "sparse_indices_component_type"), &GLTFAccessor::set_sparse_indices_component_type); + ClassDB::bind_method(D_METHOD("get_sparse_values_buffer_view"), &GLTFAccessor::get_sparse_values_buffer_view); + ClassDB::bind_method(D_METHOD("set_sparse_values_buffer_view", "sparse_values_buffer_view"), &GLTFAccessor::set_sparse_values_buffer_view); + ClassDB::bind_method(D_METHOD("get_sparse_values_byte_offset"), &GLTFAccessor::get_sparse_values_byte_offset); + ClassDB::bind_method(D_METHOD("set_sparse_values_byte_offset", "sparse_values_byte_offset"), &GLTFAccessor::set_sparse_values_byte_offset); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "buffer_view"), "set_buffer_view", "get_buffer_view"); // GLTFBufferViewIndex + ADD_PROPERTY(PropertyInfo(Variant::INT, "byte_offset"), "set_byte_offset", "get_byte_offset"); // int + ADD_PROPERTY(PropertyInfo(Variant::INT, "component_type"), "set_component_type", "get_component_type"); // int + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "normalized"), "set_normalized", "get_normalized"); // bool + ADD_PROPERTY(PropertyInfo(Variant::INT, "count"), "set_count", "get_count"); // int + ADD_PROPERTY(PropertyInfo(Variant::INT, "type"), "set_type", "get_type"); // GLTFDocument::GLTFType + ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT64_ARRAY, "min"), "set_min", "get_min"); // Vector<real_t> + ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT64_ARRAY, "max"), "set_max", "get_max"); // Vector<real_t> + ADD_PROPERTY(PropertyInfo(Variant::INT, "sparse_count"), "set_sparse_count", "get_sparse_count"); // int + ADD_PROPERTY(PropertyInfo(Variant::INT, "sparse_indices_buffer_view"), "set_sparse_indices_buffer_view", "get_sparse_indices_buffer_view"); // int + ADD_PROPERTY(PropertyInfo(Variant::INT, "sparse_indices_byte_offset"), "set_sparse_indices_byte_offset", "get_sparse_indices_byte_offset"); // int + ADD_PROPERTY(PropertyInfo(Variant::INT, "sparse_indices_component_type"), "set_sparse_indices_component_type", "get_sparse_indices_component_type"); // int + ADD_PROPERTY(PropertyInfo(Variant::INT, "sparse_values_buffer_view"), "set_sparse_values_buffer_view", "get_sparse_values_buffer_view"); // int + ADD_PROPERTY(PropertyInfo(Variant::INT, "sparse_values_byte_offset"), "set_sparse_values_byte_offset", "get_sparse_values_byte_offset"); // int +} + +GLTFBufferViewIndex GLTFAccessor::get_buffer_view() { + return buffer_view; +} + +void GLTFAccessor::set_buffer_view(GLTFBufferViewIndex p_buffer_view) { + buffer_view = p_buffer_view; +} + +int GLTFAccessor::get_byte_offset() { + return byte_offset; +} + +void GLTFAccessor::set_byte_offset(int p_byte_offset) { + byte_offset = p_byte_offset; +} + +int GLTFAccessor::get_component_type() { + return component_type; +} + +void GLTFAccessor::set_component_type(int p_component_type) { + component_type = p_component_type; +} + +bool GLTFAccessor::get_normalized() { + return normalized; +} + +void GLTFAccessor::set_normalized(bool p_normalized) { + normalized = p_normalized; +} + +int GLTFAccessor::get_count() { + return count; +} + +void GLTFAccessor::set_count(int p_count) { + count = p_count; +} + +int GLTFAccessor::get_type() { + return (int)type; +} + +void GLTFAccessor::set_type(int p_type) { + type = (GLTFDocument::GLTFType)p_type; // TODO: Register enum +} + +Vector<double> GLTFAccessor::get_min() { + return min; +} + +void GLTFAccessor::set_min(Vector<double> p_min) { + min = p_min; +} + +Vector<double> GLTFAccessor::get_max() { + return max; +} + +void GLTFAccessor::set_max(Vector<double> p_max) { + max = p_max; +} + +int GLTFAccessor::get_sparse_count() { + return sparse_count; +} + +void GLTFAccessor::set_sparse_count(int p_sparse_count) { + sparse_count = p_sparse_count; +} + +int GLTFAccessor::get_sparse_indices_buffer_view() { + return sparse_indices_buffer_view; +} + +void GLTFAccessor::set_sparse_indices_buffer_view(int p_sparse_indices_buffer_view) { + sparse_indices_buffer_view = p_sparse_indices_buffer_view; +} + +int GLTFAccessor::get_sparse_indices_byte_offset() { + return sparse_indices_byte_offset; +} + +void GLTFAccessor::set_sparse_indices_byte_offset(int p_sparse_indices_byte_offset) { + sparse_indices_byte_offset = p_sparse_indices_byte_offset; +} + +int GLTFAccessor::get_sparse_indices_component_type() { + return sparse_indices_component_type; +} + +void GLTFAccessor::set_sparse_indices_component_type(int p_sparse_indices_component_type) { + sparse_indices_component_type = p_sparse_indices_component_type; +} + +int GLTFAccessor::get_sparse_values_buffer_view() { + return sparse_values_buffer_view; +} + +void GLTFAccessor::set_sparse_values_buffer_view(int p_sparse_values_buffer_view) { + sparse_values_buffer_view = p_sparse_values_buffer_view; +} + +int GLTFAccessor::get_sparse_values_byte_offset() { + return sparse_values_byte_offset; +} + +void GLTFAccessor::set_sparse_values_byte_offset(int p_sparse_values_byte_offset) { + sparse_values_byte_offset = p_sparse_values_byte_offset; +} diff --git a/modules/gltf/gltf_accessor.h b/modules/gltf/gltf_accessor.h new file mode 100644 index 0000000000..949a601730 --- /dev/null +++ b/modules/gltf/gltf_accessor.h @@ -0,0 +1,104 @@ +/*************************************************************************/ +/* gltf_accessor.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef GLTF_ACCESSOR_H +#define GLTF_ACCESSOR_H + +#include "core/io/resource.h" +#include "gltf_document.h" + +struct GLTFAccessor : public Resource { + GDCLASS(GLTFAccessor, Resource); + friend class GLTFDocument; + +private: + GLTFBufferViewIndex buffer_view = 0; + int byte_offset = 0; + int component_type = 0; + bool normalized = false; + int count = 0; + GLTFDocument::GLTFType + type = GLTFDocument::TYPE_SCALAR; + Vector<double> min; + Vector<double> max; + int sparse_count = 0; + int sparse_indices_buffer_view = 0; + int sparse_indices_byte_offset = 0; + int sparse_indices_component_type = 0; + int sparse_values_buffer_view = 0; + int sparse_values_byte_offset = 0; + +protected: + static void _bind_methods(); + +public: + GLTFBufferViewIndex get_buffer_view(); + void set_buffer_view(GLTFBufferViewIndex p_buffer_view); + + int get_byte_offset(); + void set_byte_offset(int p_byte_offset); + + int get_component_type(); + void set_component_type(int p_component_type); + + bool get_normalized(); + void set_normalized(bool p_normalized); + + int get_count(); + void set_count(int p_count); + + int get_type(); + void set_type(int p_type); + + Vector<double> get_min(); + void set_min(Vector<double> p_min); + + Vector<double> get_max(); + void set_max(Vector<double> p_max); + + int get_sparse_count(); + void set_sparse_count(int p_sparse_count); + + int get_sparse_indices_buffer_view(); + void set_sparse_indices_buffer_view(int p_sparse_indices_buffer_view); + + int get_sparse_indices_byte_offset(); + void set_sparse_indices_byte_offset(int p_sparse_indices_byte_offset); + + int get_sparse_indices_component_type(); + void set_sparse_indices_component_type(int p_sparse_indices_component_type); + + int get_sparse_values_buffer_view(); + void set_sparse_values_buffer_view(int p_sparse_values_buffer_view); + + int get_sparse_values_byte_offset(); + void set_sparse_values_byte_offset(int p_sparse_values_byte_offset); +}; +#endif // GLTF_ACCESSOR_H diff --git a/modules/gltf/gltf_animation.cpp b/modules/gltf/gltf_animation.cpp new file mode 100644 index 0000000000..889a8e8870 --- /dev/null +++ b/modules/gltf/gltf_animation.cpp @@ -0,0 +1,53 @@ +/*************************************************************************/ +/* gltf_animation.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "gltf_animation.h" + +void GLTFAnimation::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_loop"), &GLTFAnimation::get_loop); + ClassDB::bind_method(D_METHOD("set_loop", "loop"), &GLTFAnimation::set_loop); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "get_loop"); // bool +} + +bool GLTFAnimation::get_loop() const { + return loop; +} + +void GLTFAnimation::set_loop(bool p_val) { + loop = p_val; +} + +Map<int, GLTFAnimation::Track> &GLTFAnimation::get_tracks() { + return tracks; +} + +GLTFAnimation::GLTFAnimation() { +} diff --git a/modules/gltf/gltf_animation.h b/modules/gltf/gltf_animation.h new file mode 100644 index 0000000000..a494e6bd67 --- /dev/null +++ b/modules/gltf/gltf_animation.h @@ -0,0 +1,74 @@ +/*************************************************************************/ +/* gltf_animation.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef GLTF_ANIMATION_H +#define GLTF_ANIMATION_H + +#include "core/io/resource.h" + +class GLTFAnimation : public Resource { + GDCLASS(GLTFAnimation, Resource); + +protected: + static void _bind_methods(); + +public: + enum Interpolation { + INTERP_LINEAR, + INTERP_STEP, + INTERP_CATMULLROMSPLINE, + INTERP_CUBIC_SPLINE, + }; + + template <class T> + struct Channel { + Interpolation interpolation; + Vector<float> times; + Vector<T> values; + }; + + struct Track { + Channel<Vector3> translation_track; + Channel<Quat> rotation_track; + Channel<Vector3> scale_track; + Vector<Channel<float>> weight_tracks; + }; + +public: + bool get_loop() const; + void set_loop(bool p_val); + Map<int, GLTFAnimation::Track> &get_tracks(); + GLTFAnimation(); + +private: + bool loop = false; + Map<int, Track> tracks; +}; +#endif // GLTF_ANIMATION_H diff --git a/modules/gltf/gltf_buffer_view.cpp b/modules/gltf/gltf_buffer_view.cpp new file mode 100644 index 0000000000..ba38a11c4c --- /dev/null +++ b/modules/gltf/gltf_buffer_view.cpp @@ -0,0 +1,90 @@ +/*************************************************************************/ +/* gltf_buffer_view.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "gltf_buffer_view.h" + +void GLTFBufferView::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_buffer"), &GLTFBufferView::get_buffer); + ClassDB::bind_method(D_METHOD("set_buffer", "buffer"), &GLTFBufferView::set_buffer); + ClassDB::bind_method(D_METHOD("get_byte_offset"), &GLTFBufferView::get_byte_offset); + ClassDB::bind_method(D_METHOD("set_byte_offset", "byte_offset"), &GLTFBufferView::set_byte_offset); + ClassDB::bind_method(D_METHOD("get_byte_length"), &GLTFBufferView::get_byte_length); + ClassDB::bind_method(D_METHOD("set_byte_length", "byte_length"), &GLTFBufferView::set_byte_length); + ClassDB::bind_method(D_METHOD("get_byte_stride"), &GLTFBufferView::get_byte_stride); + ClassDB::bind_method(D_METHOD("set_byte_stride", "byte_stride"), &GLTFBufferView::set_byte_stride); + ClassDB::bind_method(D_METHOD("get_indices"), &GLTFBufferView::get_indices); + ClassDB::bind_method(D_METHOD("set_indices", "indices"), &GLTFBufferView::set_indices); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "buffer"), "set_buffer", "get_buffer"); // GLTFBufferIndex + ADD_PROPERTY(PropertyInfo(Variant::INT, "byte_offset"), "set_byte_offset", "get_byte_offset"); // int + ADD_PROPERTY(PropertyInfo(Variant::INT, "byte_length"), "set_byte_length", "get_byte_length"); // int + ADD_PROPERTY(PropertyInfo(Variant::INT, "byte_stride"), "set_byte_stride", "get_byte_stride"); // int + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "indices"), "set_indices", "get_indices"); // bool +} + +GLTFBufferIndex GLTFBufferView::get_buffer() { + return buffer; +} + +void GLTFBufferView::set_buffer(GLTFBufferIndex p_buffer) { + buffer = p_buffer; +} + +int GLTFBufferView::get_byte_offset() { + return byte_offset; +} + +void GLTFBufferView::set_byte_offset(int p_byte_offset) { + byte_offset = p_byte_offset; +} + +int GLTFBufferView::get_byte_length() { + return byte_length; +} + +void GLTFBufferView::set_byte_length(int p_byte_length) { + byte_length = p_byte_length; +} + +int GLTFBufferView::get_byte_stride() { + return byte_stride; +} + +void GLTFBufferView::set_byte_stride(int p_byte_stride) { + byte_stride = p_byte_stride; +} + +bool GLTFBufferView::get_indices() { + return indices; +} + +void GLTFBufferView::set_indices(bool p_indices) { + indices = p_indices; +} diff --git a/modules/gltf/gltf_buffer_view.h b/modules/gltf/gltf_buffer_view.h new file mode 100644 index 0000000000..63af5e7c0d --- /dev/null +++ b/modules/gltf/gltf_buffer_view.h @@ -0,0 +1,68 @@ +/*************************************************************************/ +/* gltf_buffer_view.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef GLTF_BUFFER_VIEW_H +#define GLTF_BUFFER_VIEW_H + +#include "core/io/resource.h" +#include "gltf_document.h" + +class GLTFBufferView : public Resource { + GDCLASS(GLTFBufferView, Resource); + friend class GLTFDocument; + +private: + GLTFBufferIndex buffer = -1; + int byte_offset = 0; + int byte_length = 0; + int byte_stride = -1; + bool indices = false; + +protected: + static void _bind_methods(); + +public: + GLTFBufferIndex get_buffer(); + void set_buffer(GLTFBufferIndex p_buffer); + + int get_byte_offset(); + void set_byte_offset(int p_byte_offset); + + int get_byte_length(); + void set_byte_length(int p_byte_length); + + int get_byte_stride(); + void set_byte_stride(int p_byte_stride); + + bool get_indices(); + void set_indices(bool p_indices); + // matrices need to be transformed to this +}; +#endif // GLTF_BUFFER_VIEW_H diff --git a/modules/gltf/gltf_camera.cpp b/modules/gltf/gltf_camera.cpp new file mode 100644 index 0000000000..efa7c5d6d7 --- /dev/null +++ b/modules/gltf/gltf_camera.cpp @@ -0,0 +1,47 @@ +/*************************************************************************/ +/* gltf_camera.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "gltf_camera.h" + +void GLTFCamera::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_perspective"), &GLTFCamera::get_perspective); + ClassDB::bind_method(D_METHOD("set_perspective", "perspective"), &GLTFCamera::set_perspective); + ClassDB::bind_method(D_METHOD("get_fov_size"), &GLTFCamera::get_fov_size); + ClassDB::bind_method(D_METHOD("set_fov_size", "fov_size"), &GLTFCamera::set_fov_size); + ClassDB::bind_method(D_METHOD("get_zfar"), &GLTFCamera::get_zfar); + ClassDB::bind_method(D_METHOD("set_zfar", "zfar"), &GLTFCamera::set_zfar); + ClassDB::bind_method(D_METHOD("get_znear"), &GLTFCamera::get_znear); + ClassDB::bind_method(D_METHOD("set_znear", "znear"), &GLTFCamera::set_znear); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "perspective"), "set_perspective", "get_perspective"); // bool + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fov_size"), "set_fov_size", "get_fov_size"); // float + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "zfar"), "set_zfar", "get_zfar"); // float + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "znear"), "set_znear", "get_znear"); // float +} diff --git a/modules/gltf/gltf_camera.h b/modules/gltf/gltf_camera.h new file mode 100644 index 0000000000..e5c2041793 --- /dev/null +++ b/modules/gltf/gltf_camera.h @@ -0,0 +1,58 @@ +/*************************************************************************/ +/* gltf_camera.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef GLTF_CAMERA_H +#define GLTF_CAMERA_H + +#include "core/io/resource.h" + +class GLTFCamera : public Resource { + GDCLASS(GLTFCamera, Resource); + +private: + bool perspective = true; + float fov_size = 75; + float zfar = 4000; + float znear = 0.05; + +protected: + static void _bind_methods(); + +public: + bool get_perspective() const { return perspective; } + void set_perspective(bool p_val) { perspective = p_val; } + float get_fov_size() const { return fov_size; } + void set_fov_size(float p_val) { fov_size = p_val; } + float get_zfar() const { return zfar; } + void set_zfar(float p_val) { zfar = p_val; } + float get_znear() const { return znear; } + void set_znear(float p_val) { znear = p_val; } +}; +#endif // GLTF_CAMERA_H diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp new file mode 100644 index 0000000000..580bc006f5 --- /dev/null +++ b/modules/gltf/gltf_document.cpp @@ -0,0 +1,6536 @@ +/*************************************************************************/ +/* gltf_document.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "gltf_document.h" +#include "core/error/error_list.h" +#include "core/error/error_macros.h" +#include "core/variant/variant.h" +#include "gltf_accessor.h" +#include "gltf_animation.h" +#include "gltf_camera.h" +#include "gltf_light.h" +#include "gltf_mesh.h" +#include "gltf_node.h" +#include "gltf_skeleton.h" +#include "gltf_skin.h" +#include "gltf_spec_gloss.h" +#include "gltf_state.h" +#include "gltf_texture.h" + +#include <stdio.h> +#include <stdlib.h> + +#include "core/core_bind.h" +#include "core/crypto/crypto_core.h" +#include "core/io/json.h" +#include "core/math/disjoint_set.h" +#include "core/os/file_access.h" +#include "core/variant/typed_array.h" +#include "core/version.h" +#include "core/version_hash.gen.h" +#include "drivers/png/png_driver_common.h" +#include "editor/import/resource_importer_scene.h" +#include "modules/csg/csg_shape.h" +#include "modules/gridmap/grid_map.h" +#include "modules/regex/regex.h" +#include "scene/2d/node_2d.h" +#include "scene/3d/bone_attachment_3d.h" +#include "scene/3d/camera_3d.h" +#include "scene/3d/mesh_instance_3d.h" +#include "scene/3d/multimesh_instance_3d.h" +#include "scene/3d/node_3d.h" +#include "scene/3d/skeleton_3d.h" +#include "scene/animation/animation_player.h" +#include "scene/resources/surface_tool.h" +#include <limits> + +Error GLTFDocument::serialize(Ref<GLTFState> state, Node *p_root, const String &p_path) { + uint64_t begin_time = OS::get_singleton()->get_ticks_usec(); + + _convert_scene_node(state, p_root, p_root, -1, -1); + if (!state->buffers.size()) { + state->buffers.push_back(Vector<uint8_t>()); + } + + /* STEP 1 CONVERT MESH INSTANCES */ + _convert_mesh_instances(state); + + /* STEP 2 SERIALIZE CAMERAS */ + Error err = _serialize_cameras(state); + if (err != OK) { + return Error::FAILED; + } + + /* STEP 3 CREATE SKINS */ + err = _serialize_skins(state); + if (err != OK) { + return Error::FAILED; + } + /* STEP 4 CREATE BONE ATTACHMENTS */ + err = _serialize_bone_attachment(state); + if (err != OK) { + return Error::FAILED; + } + /* STEP 5 SERIALIZE MESHES (we have enough info now) */ + err = _serialize_meshes(state); + if (err != OK) { + return Error::FAILED; + } + + /* STEP 6 SERIALIZE TEXTURES */ + err = _serialize_materials(state); + if (err != OK) { + return Error::FAILED; + } + + /* STEP 7 SERIALIZE IMAGES */ + err = _serialize_images(state, p_path); + if (err != OK) { + return Error::FAILED; + } + + /* STEP 8 SERIALIZE TEXTURES */ + err = _serialize_textures(state); + if (err != OK) { + return Error::FAILED; + } + + // /* STEP 9 SERIALIZE ANIMATIONS */ + err = _serialize_animations(state); + if (err != OK) { + return Error::FAILED; + } + + /* STEP 10 SERIALIZE ACCESSORS */ + err = _encode_accessors(state); + if (err != OK) { + return Error::FAILED; + } + + for (GLTFBufferViewIndex i = 0; i < state->buffer_views.size(); i++) { + state->buffer_views.write[i]->buffer = 0; + } + + /* STEP 11 SERIALIZE BUFFER VIEWS */ + err = _encode_buffer_views(state); + if (err != OK) { + return Error::FAILED; + } + + /* STEP 12 SERIALIZE NODES */ + err = _serialize_nodes(state); + if (err != OK) { + return Error::FAILED; + } + + /* STEP 13 SERIALIZE SCENE */ + err = _serialize_scenes(state); + if (err != OK) { + return Error::FAILED; + } + + /* STEP 14 SERIALIZE SCENE */ + err = _serialize_lights(state); + if (err != OK) { + return Error::FAILED; + } + + /* STEP 15 SERIALIZE EXTENSIONS */ + err = _serialize_extensions(state); + if (err != OK) { + return Error::FAILED; + } + + /* STEP 16 SERIALIZE VERSION */ + err = _serialize_version(state); + if (err != OK) { + return Error::FAILED; + } + + /* STEP 17 SERIALIZE FILE */ + err = _serialize_file(state, p_path); + if (err != OK) { + return Error::FAILED; + } + uint64_t elapsed = OS::get_singleton()->get_ticks_usec() - begin_time; + float elapsed_sec = double(elapsed) / 1000000.0; + elapsed_sec = Math::snapped(elapsed_sec, 0.01f); + print_line("glTF: Export time elapsed seconds " + rtos(elapsed_sec).pad_decimals(2)); + + return OK; +} + +Error GLTFDocument::_serialize_extensions(Ref<GLTFState> state) const { + const String texture_transform = "KHR_texture_transform"; + const String punctual_lights = "KHR_lights_punctual"; + Array extensions_used; + extensions_used.push_back(punctual_lights); + extensions_used.push_back(texture_transform); + state->json["extensionsUsed"] = extensions_used; + Array extensions_required; + extensions_required.push_back(texture_transform); + state->json["extensionsRequired"] = extensions_required; + return OK; +} + +Error GLTFDocument::_serialize_scenes(Ref<GLTFState> state) { + Array scenes; + const int loaded_scene = 0; + state->json["scene"] = loaded_scene; + + if (state->nodes.size()) { + Dictionary s; + if (!state->scene_name.is_empty()) { + s["name"] = state->scene_name; + } + + Array nodes; + nodes.push_back(0); + s["nodes"] = nodes; + scenes.push_back(s); + } + state->json["scenes"] = scenes; + + return OK; +} + +Error GLTFDocument::_parse_json(const String &p_path, Ref<GLTFState> state) { + Error err; + FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err); + if (!f) { + return err; + } + + Vector<uint8_t> array; + array.resize(f->get_len()); + f->get_buffer(array.ptrw(), array.size()); + String text; + text.parse_utf8((const char *)array.ptr(), array.size()); + + String err_txt; + int err_line; + Variant v; + err = JSON::parse(text, v, err_txt, err_line); + if (err != OK) { + _err_print_error("", p_path.utf8().get_data(), err_line, err_txt.utf8().get_data(), ERR_HANDLER_SCRIPT); + return err; + } + state->json = v; + + return OK; +} + +Error GLTFDocument::_serialize_bone_attachment(Ref<GLTFState> state) { + for (int skeleton_i = 0; skeleton_i < state->skeletons.size(); skeleton_i++) { + for (int attachment_i = 0; attachment_i < state->skeletons[skeleton_i]->bone_attachments.size(); attachment_i++) { + BoneAttachment3D *bone_attachment = state->skeletons[skeleton_i]->bone_attachments[attachment_i]; + String bone_name = bone_attachment->get_bone_name(); + bone_name = _sanitize_bone_name(bone_name); + int32_t bone = state->skeletons[skeleton_i]->godot_skeleton->find_bone(bone_name); + ERR_CONTINUE(bone == -1); + for (int skin_i = 0; skin_i < state->skins.size(); skin_i++) { + if (state->skins[skin_i]->skeleton != skeleton_i) { + continue; + } + + for (int node_i = 0; node_i < bone_attachment->get_child_count(); node_i++) { + ERR_CONTINUE(bone >= state->skins[skin_i]->joints.size()); + _convert_scene_node(state, bone_attachment->get_child(node_i), bone_attachment->get_owner(), state->skins[skin_i]->joints[bone], 0); + } + break; + } + } + } + return OK; +} + +Error GLTFDocument::_parse_glb(const String &p_path, Ref<GLTFState> state) { + Error err; + FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err); + if (!f) { + return err; + } + + uint32_t magic = f->get_32(); + ERR_FAIL_COND_V(magic != 0x46546C67, ERR_FILE_UNRECOGNIZED); //glTF + f->get_32(); // version + f->get_32(); // length + + uint32_t chunk_length = f->get_32(); + uint32_t chunk_type = f->get_32(); + + ERR_FAIL_COND_V(chunk_type != 0x4E4F534A, ERR_PARSE_ERROR); //JSON + Vector<uint8_t> json_data; + json_data.resize(chunk_length); + uint32_t len = f->get_buffer(json_data.ptrw(), chunk_length); + ERR_FAIL_COND_V(len != chunk_length, ERR_FILE_CORRUPT); + + String text; + text.parse_utf8((const char *)json_data.ptr(), json_data.size()); + + String err_txt; + int err_line; + Variant v; + err = JSON::parse(text, v, err_txt, err_line); + if (err != OK) { + _err_print_error("", p_path.utf8().get_data(), err_line, err_txt.utf8().get_data(), ERR_HANDLER_SCRIPT); + return err; + } + + state->json = v; + + //data? + + chunk_length = f->get_32(); + chunk_type = f->get_32(); + + if (f->eof_reached()) { + return OK; //all good + } + + ERR_FAIL_COND_V(chunk_type != 0x004E4942, ERR_PARSE_ERROR); //BIN + + state->glb_data.resize(chunk_length); + len = f->get_buffer(state->glb_data.ptrw(), chunk_length); + ERR_FAIL_COND_V(len != chunk_length, ERR_FILE_CORRUPT); + + return OK; +} + +static Array _vec3_to_arr(const Vector3 &p_vec3) { + Array array; + array.resize(3); + array[0] = p_vec3.x; + array[1] = p_vec3.y; + array[2] = p_vec3.z; + return array; +} + +static Vector3 _arr_to_vec3(const Array &p_array) { + ERR_FAIL_COND_V(p_array.size() != 3, Vector3()); + return Vector3(p_array[0], p_array[1], p_array[2]); +} + +static Array _quat_to_array(const Quat &p_quat) { + Array array; + array.resize(4); + array[0] = p_quat.x; + array[1] = p_quat.y; + array[2] = p_quat.z; + array[3] = p_quat.w; + return array; +} + +static Quat _arr_to_quat(const Array &p_array) { + ERR_FAIL_COND_V(p_array.size() != 4, Quat()); + return Quat(p_array[0], p_array[1], p_array[2], p_array[3]); +} + +static Transform _arr_to_xform(const Array &p_array) { + ERR_FAIL_COND_V(p_array.size() != 16, Transform()); + + Transform xform; + xform.basis.set_axis(Vector3::AXIS_X, Vector3(p_array[0], p_array[1], p_array[2])); + xform.basis.set_axis(Vector3::AXIS_Y, Vector3(p_array[4], p_array[5], p_array[6])); + xform.basis.set_axis(Vector3::AXIS_Z, Vector3(p_array[8], p_array[9], p_array[10])); + xform.set_origin(Vector3(p_array[12], p_array[13], p_array[14])); + + return xform; +} + +static Vector<real_t> _xform_to_array(const Transform p_transform) { + Vector<real_t> array; + array.resize(16); + Vector3 axis_x = p_transform.get_basis().get_axis(Vector3::AXIS_X); + array.write[0] = axis_x.x; + array.write[1] = axis_x.y; + array.write[2] = axis_x.z; + array.write[3] = 0.0f; + Vector3 axis_y = p_transform.get_basis().get_axis(Vector3::AXIS_Y); + array.write[4] = axis_y.x; + array.write[5] = axis_y.y; + array.write[6] = axis_y.z; + array.write[7] = 0.0f; + Vector3 axis_z = p_transform.get_basis().get_axis(Vector3::AXIS_Z); + array.write[8] = axis_z.x; + array.write[9] = axis_z.y; + array.write[10] = axis_z.z; + array.write[11] = 0.0f; + Vector3 origin = p_transform.get_origin(); + array.write[12] = origin.x; + array.write[13] = origin.y; + array.write[14] = origin.z; + array.write[15] = 1.0f; + return array; +} + +Error GLTFDocument::_serialize_nodes(Ref<GLTFState> state) { + Array nodes; + for (int i = 0; i < state->nodes.size(); i++) { + Dictionary node; + Ref<GLTFNode> n = state->nodes[i]; + Dictionary extensions; + node["extensions"] = extensions; + if (!n->get_name().is_empty()) { + node["name"] = n->get_name(); + } + if (n->camera != -1) { + node["camera"] = n->camera; + } + if (n->light != -1) { + Dictionary lights_punctual; + extensions["KHR_lights_punctual"] = lights_punctual; + lights_punctual["light"] = n->light; + } + if (n->mesh != -1) { + node["mesh"] = n->mesh; + } + if (n->skin != -1) { + node["skin"] = n->skin; + } + if (n->skeleton != -1 && n->skin < 0) { + } + if (n->xform != Transform()) { + node["matrix"] = _xform_to_array(n->xform); + } + + if (!n->rotation.is_equal_approx(Quat())) { + node["rotation"] = _quat_to_array(n->rotation); + } + + if (!n->scale.is_equal_approx(Vector3(1.0f, 1.0f, 1.0f))) { + node["scale"] = _vec3_to_arr(n->scale); + } + + if (!n->translation.is_equal_approx(Vector3())) { + node["translation"] = _vec3_to_arr(n->translation); + } + if (n->children.size()) { + Array children; + for (int j = 0; j < n->children.size(); j++) { + children.push_back(n->children[j]); + } + node["children"] = children; + } + nodes.push_back(node); + } + state->json["nodes"] = nodes; + return OK; +} + +String GLTFDocument::_sanitize_scene_name(const String &name) { + RegEx regex("([^a-zA-Z0-9_ -]+)"); + String p_name = regex.sub(name, "", true); + return p_name; +} + +String GLTFDocument::_gen_unique_name(Ref<GLTFState> state, const String &p_name) { + const String s_name = _sanitize_scene_name(p_name); + + String name; + int index = 1; + while (true) { + name = s_name; + + if (index > 1) { + name += " " + itos(index); + } + if (!state->unique_names.has(name)) { + break; + } + index++; + } + + state->unique_names.insert(name); + + return name; +} + +String GLTFDocument::_sanitize_bone_name(const String &name) { + String p_name = name.camelcase_to_underscore(true); + + RegEx pattern_nocolon(":"); + p_name = pattern_nocolon.sub(p_name, "_", true); + + RegEx pattern_noslash("/"); + p_name = pattern_noslash.sub(p_name, "_", true); + + RegEx pattern_nospace(" +"); + p_name = pattern_nospace.sub(p_name, "_", true); + + RegEx pattern_multiple("_+"); + p_name = pattern_multiple.sub(p_name, "_", true); + + RegEx pattern_padded("0+(\\d+)"); + p_name = pattern_padded.sub(p_name, "$1", true); + + return p_name; +} + +String GLTFDocument::_gen_unique_bone_name(Ref<GLTFState> state, const GLTFSkeletonIndex skel_i, const String &p_name) { + String s_name = _sanitize_bone_name(p_name); + if (s_name.is_empty()) { + s_name = "bone"; + } + String name; + int index = 1; + while (true) { + name = s_name; + + if (index > 1) { + name += "_" + itos(index); + } + if (!state->skeletons[skel_i]->unique_names.has(name)) { + break; + } + index++; + } + + state->skeletons.write[skel_i]->unique_names.insert(name); + + return name; +} + +Error GLTFDocument::_parse_scenes(Ref<GLTFState> state) { + ERR_FAIL_COND_V(!state->json.has("scenes"), ERR_FILE_CORRUPT); + const Array &scenes = state->json["scenes"]; + int loaded_scene = 0; + if (state->json.has("scene")) { + loaded_scene = state->json["scene"]; + } else { + WARN_PRINT("The load-time scene is not defined in the glTF2 file. Picking the first scene."); + } + + if (scenes.size()) { + ERR_FAIL_COND_V(loaded_scene >= scenes.size(), ERR_FILE_CORRUPT); + const Dictionary &s = scenes[loaded_scene]; + ERR_FAIL_COND_V(!s.has("nodes"), ERR_UNAVAILABLE); + const Array &nodes = s["nodes"]; + for (int j = 0; j < nodes.size(); j++) { + state->root_nodes.push_back(nodes[j]); + } + + if (s.has("name") && s["name"] != "") { + state->scene_name = _gen_unique_name(state, s["name"]); + } else { + state->scene_name = _gen_unique_name(state, "Scene"); + } + } + + return OK; +} + +Error GLTFDocument::_parse_nodes(Ref<GLTFState> state) { + ERR_FAIL_COND_V(!state->json.has("nodes"), ERR_FILE_CORRUPT); + const Array &nodes = state->json["nodes"]; + for (int i = 0; i < nodes.size(); i++) { + Ref<GLTFNode> node; + node.instance(); + const Dictionary &n = nodes[i]; + + if (n.has("name")) { + node->set_name(n["name"]); + } + if (n.has("camera")) { + node->camera = n["camera"]; + } + if (n.has("mesh")) { + node->mesh = n["mesh"]; + } + if (n.has("skin")) { + node->skin = n["skin"]; + } + if (n.has("matrix")) { + node->xform = _arr_to_xform(n["matrix"]); + } else { + if (n.has("translation")) { + node->translation = _arr_to_vec3(n["translation"]); + } + if (n.has("rotation")) { + node->rotation = _arr_to_quat(n["rotation"]); + } + if (n.has("scale")) { + node->scale = _arr_to_vec3(n["scale"]); + } + + node->xform.basis.set_quat_scale(node->rotation, node->scale); + node->xform.origin = node->translation; + } + + if (n.has("extensions")) { + Dictionary extensions = n["extensions"]; + if (extensions.has("KHR_lights_punctual")) { + Dictionary lights_punctual = extensions["KHR_lights_punctual"]; + if (lights_punctual.has("light")) { + GLTFLightIndex light = lights_punctual["light"]; + node->light = light; + } + } + } + + if (n.has("children")) { + const Array &children = n["children"]; + for (int j = 0; j < children.size(); j++) { + node->children.push_back(children[j]); + } + } + + state->nodes.push_back(node); + } + + // build the hierarchy + for (GLTFNodeIndex node_i = 0; node_i < state->nodes.size(); node_i++) { + for (int j = 0; j < state->nodes[node_i]->children.size(); j++) { + GLTFNodeIndex child_i = state->nodes[node_i]->children[j]; + + ERR_FAIL_INDEX_V(child_i, state->nodes.size(), ERR_FILE_CORRUPT); + ERR_CONTINUE(state->nodes[child_i]->parent != -1); //node already has a parent, wtf. + + state->nodes.write[child_i]->parent = node_i; + } + } + + _compute_node_heights(state); + + return OK; +} + +void GLTFDocument::_compute_node_heights(Ref<GLTFState> state) { + state->root_nodes.clear(); + for (GLTFNodeIndex node_i = 0; node_i < state->nodes.size(); ++node_i) { + Ref<GLTFNode> node = state->nodes[node_i]; + node->height = 0; + + GLTFNodeIndex current_i = node_i; + while (current_i >= 0) { + const GLTFNodeIndex parent_i = state->nodes[current_i]->parent; + if (parent_i >= 0) { + ++node->height; + } + current_i = parent_i; + } + + if (node->height == 0) { + state->root_nodes.push_back(node_i); + } + } +} + +static Vector<uint8_t> _parse_base64_uri(const String &uri) { + int start = uri.find(","); + ERR_FAIL_COND_V(start == -1, Vector<uint8_t>()); + + CharString substr = uri.right(start + 1).ascii(); + + int strlen = substr.length(); + + Vector<uint8_t> buf; + buf.resize(strlen / 4 * 3 + 1 + 1); + + size_t len = 0; + ERR_FAIL_COND_V(CryptoCore::b64_decode(buf.ptrw(), buf.size(), &len, (unsigned char *)substr.get_data(), strlen) != OK, Vector<uint8_t>()); + + buf.resize(len); + + return buf; +} +Error GLTFDocument::_encode_buffer_glb(Ref<GLTFState> state, const String &p_path) { + print_verbose("glTF: Total buffers: " + itos(state->buffers.size())); + + if (!state->buffers.size()) { + return OK; + } + Array buffers; + if (state->buffers.size()) { + Vector<uint8_t> buffer_data = state->buffers[0]; + Dictionary gltf_buffer; + + gltf_buffer["byteLength"] = buffer_data.size(); + buffers.push_back(gltf_buffer); + } + + for (GLTFBufferIndex i = 1; i < state->buffers.size() - 1; i++) { + Vector<uint8_t> buffer_data = state->buffers[i]; + Dictionary gltf_buffer; + String filename = p_path.get_basename().get_file() + itos(i) + ".bin"; + String path = p_path.get_base_dir() + "/" + filename; + Error err; + FileAccessRef f = FileAccess::open(path, FileAccess::WRITE, &err); + if (!f) { + return err; + } + if (buffer_data.size() == 0) { + return OK; + } + f->create(FileAccess::ACCESS_RESOURCES); + f->store_buffer(buffer_data.ptr(), buffer_data.size()); + f->close(); + gltf_buffer["uri"] = filename; + gltf_buffer["byteLength"] = buffer_data.size(); + buffers.push_back(gltf_buffer); + } + state->json["buffers"] = buffers; + + return OK; +} + +Error GLTFDocument::_encode_buffer_bins(Ref<GLTFState> state, const String &p_path) { + print_verbose("glTF: Total buffers: " + itos(state->buffers.size())); + + if (!state->buffers.size()) { + return OK; + } + Array buffers; + + for (GLTFBufferIndex i = 0; i < state->buffers.size(); i++) { + Vector<uint8_t> buffer_data = state->buffers[i]; + Dictionary gltf_buffer; + String filename = p_path.get_basename().get_file() + itos(i) + ".bin"; + String path = p_path.get_base_dir() + "/" + filename; + Error err; + FileAccessRef f = FileAccess::open(path, FileAccess::WRITE, &err); + if (!f) { + return err; + } + if (buffer_data.size() == 0) { + return OK; + } + f->create(FileAccess::ACCESS_RESOURCES); + f->store_buffer(buffer_data.ptr(), buffer_data.size()); + f->close(); + gltf_buffer["uri"] = filename; + gltf_buffer["byteLength"] = buffer_data.size(); + buffers.push_back(gltf_buffer); + } + state->json["buffers"] = buffers; + + return OK; +} + +Error GLTFDocument::_parse_buffers(Ref<GLTFState> state, const String &p_base_path) { + if (!state->json.has("buffers")) { + return OK; + } + + const Array &buffers = state->json["buffers"]; + for (GLTFBufferIndex i = 0; i < buffers.size(); i++) { + if (i == 0 && state->glb_data.size()) { + state->buffers.push_back(state->glb_data); + + } else { + const Dictionary &buffer = buffers[i]; + if (buffer.has("uri")) { + Vector<uint8_t> buffer_data; + String uri = buffer["uri"]; + + if (uri.begins_with("data:")) { // Embedded data using base64. + // Validate data MIME types and throw an error if it's one we don't know/support. + if (!uri.begins_with("data:application/octet-stream;base64") && + !uri.begins_with("data:application/gltf-buffer;base64")) { + ERR_PRINT("glTF: Got buffer with an unknown URI data type: " + uri); + } + buffer_data = _parse_base64_uri(uri); + } else { // Relative path to an external image file. + uri = p_base_path.plus_file(uri).replace("\\", "/"); // Fix for Windows. + buffer_data = FileAccess::get_file_as_array(uri); + ERR_FAIL_COND_V_MSG(buffer.size() == 0, ERR_PARSE_ERROR, "glTF: Couldn't load binary file as an array: " + uri); + } + + ERR_FAIL_COND_V(!buffer.has("byteLength"), ERR_PARSE_ERROR); + int byteLength = buffer["byteLength"]; + ERR_FAIL_COND_V(byteLength < buffer_data.size(), ERR_PARSE_ERROR); + state->buffers.push_back(buffer_data); + } + } + } + + print_verbose("glTF: Total buffers: " + itos(state->buffers.size())); + + return OK; +} + +Error GLTFDocument::_encode_buffer_views(Ref<GLTFState> state) { + Array buffers; + for (GLTFBufferViewIndex i = 0; i < state->buffer_views.size(); i++) { + Dictionary d; + + Ref<GLTFBufferView> buffer_view = state->buffer_views[i]; + + d["buffer"] = buffer_view->buffer; + d["byteLength"] = buffer_view->byte_length; + + d["byteOffset"] = buffer_view->byte_offset; + + if (buffer_view->byte_stride != -1) { + d["byteStride"] = buffer_view->byte_stride; + } + + // TODO Sparse + // d["target"] = buffer_view->indices; + + ERR_FAIL_COND_V(!d.has("buffer"), ERR_INVALID_DATA); + ERR_FAIL_COND_V(!d.has("byteLength"), ERR_INVALID_DATA); + buffers.push_back(d); + } + print_verbose("glTF: Total buffer views: " + itos(state->buffer_views.size())); + state->json["bufferViews"] = buffers; + return OK; +} + +Error GLTFDocument::_parse_buffer_views(Ref<GLTFState> state) { + ERR_FAIL_COND_V(!state->json.has("bufferViews"), ERR_FILE_CORRUPT); + const Array &buffers = state->json["bufferViews"]; + for (GLTFBufferViewIndex i = 0; i < buffers.size(); i++) { + const Dictionary &d = buffers[i]; + + Ref<GLTFBufferView> buffer_view; + buffer_view.instance(); + + ERR_FAIL_COND_V(!d.has("buffer"), ERR_PARSE_ERROR); + buffer_view->buffer = d["buffer"]; + ERR_FAIL_COND_V(!d.has("byteLength"), ERR_PARSE_ERROR); + buffer_view->byte_length = d["byteLength"]; + + if (d.has("byteOffset")) { + buffer_view->byte_offset = d["byteOffset"]; + } + + if (d.has("byteStride")) { + buffer_view->byte_stride = d["byteStride"]; + } + + if (d.has("target")) { + const int target = d["target"]; + buffer_view->indices = target == GLTFDocument::ELEMENT_ARRAY_BUFFER; + } + + state->buffer_views.push_back(buffer_view); + } + + print_verbose("glTF: Total buffer views: " + itos(state->buffer_views.size())); + + return OK; +} + +Error GLTFDocument::_encode_accessors(Ref<GLTFState> state) { + Array accessors; + for (GLTFAccessorIndex i = 0; i < state->accessors.size(); i++) { + Dictionary d; + + Ref<GLTFAccessor> accessor = state->accessors[i]; + d["componentType"] = accessor->component_type; + d["count"] = accessor->count; + d["type"] = _get_accessor_type_name(accessor->type); + d["byteOffset"] = accessor->byte_offset; + d["normalized"] = accessor->normalized; + d["max"] = accessor->max; + d["min"] = accessor->min; + d["bufferView"] = accessor->buffer_view; //optional because it may be sparse... + + // Dictionary s; + // s["count"] = accessor->sparse_count; + // ERR_FAIL_COND_V(!s.has("count"), ERR_PARSE_ERROR); + + // s["indices"] = accessor->sparse_accessors; + // ERR_FAIL_COND_V(!s.has("indices"), ERR_PARSE_ERROR); + + // Dictionary si; + + // si["bufferView"] = accessor->sparse_indices_buffer_view; + + // ERR_FAIL_COND_V(!si.has("bufferView"), ERR_PARSE_ERROR); + // si["componentType"] = accessor->sparse_indices_component_type; + + // if (si.has("byteOffset")) { + // si["byteOffset"] = accessor->sparse_indices_byte_offset; + // } + + // ERR_FAIL_COND_V(!si.has("componentType"), ERR_PARSE_ERROR); + // s["indices"] = si; + // Dictionary sv; + + // sv["bufferView"] = accessor->sparse_values_buffer_view; + // if (sv.has("byteOffset")) { + // sv["byteOffset"] = accessor->sparse_values_byte_offset; + // } + // ERR_FAIL_COND_V(!sv.has("bufferView"), ERR_PARSE_ERROR); + // s["values"] = sv; + // ERR_FAIL_COND_V(!s.has("values"), ERR_PARSE_ERROR); + // d["sparse"] = s; + accessors.push_back(d); + } + + state->json["accessors"] = accessors; + ERR_FAIL_COND_V(!state->json.has("accessors"), ERR_FILE_CORRUPT); + print_verbose("glTF: Total accessors: " + itos(state->accessors.size())); + + return OK; +} + +String GLTFDocument::_get_accessor_type_name(const GLTFDocument::GLTFType p_type) { + if (p_type == GLTFDocument::TYPE_SCALAR) { + return "SCALAR"; + } + if (p_type == GLTFDocument::TYPE_VEC2) { + return "VEC2"; + } + if (p_type == GLTFDocument::TYPE_VEC3) { + return "VEC3"; + } + if (p_type == GLTFDocument::TYPE_VEC4) { + return "VEC4"; + } + + if (p_type == GLTFDocument::TYPE_MAT2) { + return "MAT2"; + } + if (p_type == GLTFDocument::TYPE_MAT3) { + return "MAT3"; + } + if (p_type == GLTFDocument::TYPE_MAT4) { + return "MAT4"; + } + ERR_FAIL_V("SCALAR"); +} + +GLTFDocument::GLTFType GLTFDocument::_get_type_from_str(const String &p_string) { + if (p_string == "SCALAR") + return GLTFDocument::TYPE_SCALAR; + + if (p_string == "VEC2") + return GLTFDocument::TYPE_VEC2; + if (p_string == "VEC3") + return GLTFDocument::TYPE_VEC3; + if (p_string == "VEC4") + return GLTFDocument::TYPE_VEC4; + + if (p_string == "MAT2") + return GLTFDocument::TYPE_MAT2; + if (p_string == "MAT3") + return GLTFDocument::TYPE_MAT3; + if (p_string == "MAT4") + return GLTFDocument::TYPE_MAT4; + + ERR_FAIL_V(GLTFDocument::TYPE_SCALAR); +} + +Error GLTFDocument::_parse_accessors(Ref<GLTFState> state) { + ERR_FAIL_COND_V(!state->json.has("accessors"), ERR_FILE_CORRUPT); + const Array &accessors = state->json["accessors"]; + for (GLTFAccessorIndex i = 0; i < accessors.size(); i++) { + const Dictionary &d = accessors[i]; + + Ref<GLTFAccessor> accessor; + accessor.instance(); + + ERR_FAIL_COND_V(!d.has("componentType"), ERR_PARSE_ERROR); + accessor->component_type = d["componentType"]; + ERR_FAIL_COND_V(!d.has("count"), ERR_PARSE_ERROR); + accessor->count = d["count"]; + ERR_FAIL_COND_V(!d.has("type"), ERR_PARSE_ERROR); + accessor->type = _get_type_from_str(d["type"]); + + if (d.has("bufferView")) { + accessor->buffer_view = d["bufferView"]; //optional because it may be sparse... + } + + if (d.has("byteOffset")) { + accessor->byte_offset = d["byteOffset"]; + } + + if (d.has("normalized")) { + accessor->normalized = d["normalized"]; + } + + if (d.has("max")) { + accessor->max = d["max"]; + } + + if (d.has("min")) { + accessor->min = d["min"]; + } + + if (d.has("sparse")) { + //eeh.. + + const Dictionary &s = d["sparse"]; + + ERR_FAIL_COND_V(!s.has("count"), ERR_PARSE_ERROR); + accessor->sparse_count = s["count"]; + ERR_FAIL_COND_V(!s.has("indices"), ERR_PARSE_ERROR); + const Dictionary &si = s["indices"]; + + ERR_FAIL_COND_V(!si.has("bufferView"), ERR_PARSE_ERROR); + accessor->sparse_indices_buffer_view = si["bufferView"]; + ERR_FAIL_COND_V(!si.has("componentType"), ERR_PARSE_ERROR); + accessor->sparse_indices_component_type = si["componentType"]; + + if (si.has("byteOffset")) { + accessor->sparse_indices_byte_offset = si["byteOffset"]; + } + + ERR_FAIL_COND_V(!s.has("values"), ERR_PARSE_ERROR); + const Dictionary &sv = s["values"]; + + ERR_FAIL_COND_V(!sv.has("bufferView"), ERR_PARSE_ERROR); + accessor->sparse_values_buffer_view = sv["bufferView"]; + if (sv.has("byteOffset")) { + accessor->sparse_values_byte_offset = sv["byteOffset"]; + } + } + + state->accessors.push_back(accessor); + } + + print_verbose("glTF: Total accessors: " + itos(state->accessors.size())); + + return OK; +} + +double GLTFDocument::_filter_number(double p_float) { + if (Math::is_nan(p_float)) { + return 0.0f; + } + return p_float; +} + +String GLTFDocument::_get_component_type_name(const uint32_t p_component) { + switch (p_component) { + case GLTFDocument::COMPONENT_TYPE_BYTE: + return "Byte"; + case GLTFDocument::COMPONENT_TYPE_UNSIGNED_BYTE: + return "UByte"; + case GLTFDocument::COMPONENT_TYPE_SHORT: + return "Short"; + case GLTFDocument::COMPONENT_TYPE_UNSIGNED_SHORT: + return "UShort"; + case GLTFDocument::COMPONENT_TYPE_INT: + return "Int"; + case GLTFDocument::COMPONENT_TYPE_FLOAT: + return "Float"; + } + + return "<Error>"; +} + +String GLTFDocument::_get_type_name(const GLTFType p_component) { + static const char *names[] = { + "float", + "vec2", + "vec3", + "vec4", + "mat2", + "mat3", + "mat4" + }; + + return names[p_component]; +} + +Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> state, const double *src, const int count, const GLTFType type, const int component_type, const bool normalized, const int byte_offset, const bool for_vertex, GLTFBufferViewIndex &r_accessor) { + const int component_count_for_type[7] = { + 1, 2, 3, 4, 4, 9, 16 + }; + + const int component_count = component_count_for_type[type]; + const int component_size = _get_component_type_size(component_type); + ERR_FAIL_COND_V(component_size == 0, FAILED); + + int skip_every = 0; + int skip_bytes = 0; + //special case of alignments, as described in spec + switch (component_type) { + case COMPONENT_TYPE_BYTE: + case COMPONENT_TYPE_UNSIGNED_BYTE: { + if (type == TYPE_MAT2) { + skip_every = 2; + skip_bytes = 2; + } + if (type == TYPE_MAT3) { + skip_every = 3; + skip_bytes = 1; + } + } break; + case COMPONENT_TYPE_SHORT: + case COMPONENT_TYPE_UNSIGNED_SHORT: { + if (type == TYPE_MAT3) { + skip_every = 6; + skip_bytes = 4; + } + } break; + default: { + } + } + + Ref<GLTFBufferView> bv; + bv.instance(); + const uint32_t offset = bv->byte_offset = byte_offset; + Vector<uint8_t> &gltf_buffer = state->buffers.write[0]; + + int stride = _get_component_type_size(component_type); + if (for_vertex && stride % 4) { + stride += 4 - (stride % 4); //according to spec must be multiple of 4 + } + //use to debug + print_verbose("glTF: encoding type " + _get_type_name(type) + " component type: " + _get_component_type_name(component_type) + " stride: " + itos(stride) + " amount " + itos(count)); + + print_verbose("glTF: encoding accessor offset " + itos(byte_offset) + " view offset: " + itos(bv->byte_offset) + " total buffer len: " + itos(gltf_buffer.size()) + " view len " + itos(bv->byte_length)); + + const int buffer_end = (stride * (count - 1)) + _get_component_type_size(component_type); + // TODO define bv->byte_stride + bv->byte_offset = gltf_buffer.size(); + + switch (component_type) { + case COMPONENT_TYPE_BYTE: { + Vector<int8_t> buffer; + buffer.resize(count * component_count); + int32_t dst_i = 0; + for (int i = 0; i < count; i++) { + for (int j = 0; j < component_count; j++) { + if (skip_every && j > 0 && (j % skip_every) == 0) { + dst_i += skip_bytes; + } + double d = *src; + if (normalized) { + buffer.write[dst_i] = d * 128.0; + } else { + buffer.write[dst_i] = d; + } + src++; + dst_i++; + } + } + int64_t old_size = gltf_buffer.size(); + gltf_buffer.resize(old_size + (buffer.size() * sizeof(int8_t))); + copymem(gltf_buffer.ptrw() + old_size, buffer.ptrw(), buffer.size() * sizeof(int8_t)); + bv->byte_length = buffer.size() * sizeof(int8_t); + } break; + case COMPONENT_TYPE_UNSIGNED_BYTE: { + Vector<uint8_t> buffer; + buffer.resize(count * component_count); + int32_t dst_i = 0; + for (int i = 0; i < count; i++) { + for (int j = 0; j < component_count; j++) { + if (skip_every && j > 0 && (j % skip_every) == 0) { + dst_i += skip_bytes; + } + double d = *src; + if (normalized) { + buffer.write[dst_i] = d * 255.0; + } else { + buffer.write[dst_i] = d; + } + src++; + dst_i++; + } + } + gltf_buffer.append_array(buffer); + bv->byte_length = buffer.size() * sizeof(uint8_t); + } break; + case COMPONENT_TYPE_SHORT: { + Vector<int16_t> buffer; + buffer.resize(count * component_count); + int32_t dst_i = 0; + for (int i = 0; i < count; i++) { + for (int j = 0; j < component_count; j++) { + if (skip_every && j > 0 && (j % skip_every) == 0) { + dst_i += skip_bytes; + } + double d = *src; + if (normalized) { + buffer.write[dst_i] = d * 32768.0; + } else { + buffer.write[dst_i] = d; + } + src++; + dst_i++; + } + } + int64_t old_size = gltf_buffer.size(); + gltf_buffer.resize(old_size + (buffer.size() * sizeof(int16_t))); + copymem(gltf_buffer.ptrw() + old_size, buffer.ptrw(), buffer.size() * sizeof(int16_t)); + bv->byte_length = buffer.size() * sizeof(int16_t); + } break; + case COMPONENT_TYPE_UNSIGNED_SHORT: { + Vector<uint16_t> buffer; + buffer.resize(count * component_count); + int32_t dst_i = 0; + for (int i = 0; i < count; i++) { + for (int j = 0; j < component_count; j++) { + if (skip_every && j > 0 && (j % skip_every) == 0) { + dst_i += skip_bytes; + } + double d = *src; + if (normalized) { + buffer.write[dst_i] = d * 65535.0; + } else { + buffer.write[dst_i] = d; + } + src++; + dst_i++; + } + } + int64_t old_size = gltf_buffer.size(); + gltf_buffer.resize(old_size + (buffer.size() * sizeof(uint16_t))); + copymem(gltf_buffer.ptrw() + old_size, buffer.ptrw(), buffer.size() * sizeof(uint16_t)); + bv->byte_length = buffer.size() * sizeof(uint16_t); + } break; + case COMPONENT_TYPE_INT: { + Vector<int> buffer; + buffer.resize(count * component_count); + int32_t dst_i = 0; + for (int i = 0; i < count; i++) { + for (int j = 0; j < component_count; j++) { + if (skip_every && j > 0 && (j % skip_every) == 0) { + dst_i += skip_bytes; + } + double d = *src; + buffer.write[dst_i] = d; + src++; + dst_i++; + } + } + int64_t old_size = gltf_buffer.size(); + gltf_buffer.resize(old_size + (buffer.size() * sizeof(int32_t))); + copymem(gltf_buffer.ptrw() + old_size, buffer.ptrw(), buffer.size() * sizeof(int32_t)); + bv->byte_length = buffer.size() * sizeof(int32_t); + } break; + case COMPONENT_TYPE_FLOAT: { + Vector<float> buffer; + buffer.resize(count * component_count); + int32_t dst_i = 0; + for (int i = 0; i < count; i++) { + for (int j = 0; j < component_count; j++) { + if (skip_every && j > 0 && (j % skip_every) == 0) { + dst_i += skip_bytes; + } + double d = *src; + buffer.write[dst_i] = d; + src++; + dst_i++; + } + } + int64_t old_size = gltf_buffer.size(); + gltf_buffer.resize(old_size + (buffer.size() * sizeof(float))); + copymem(gltf_buffer.ptrw() + old_size, buffer.ptrw(), buffer.size() * sizeof(float)); + bv->byte_length = buffer.size() * sizeof(float); + } break; + } + ERR_FAIL_COND_V(buffer_end > bv->byte_length, ERR_INVALID_DATA); + + ERR_FAIL_COND_V((int)(offset + buffer_end) > gltf_buffer.size(), ERR_INVALID_DATA); + r_accessor = bv->buffer = state->buffer_views.size(); + state->buffer_views.push_back(bv); + return OK; +} + +Error GLTFDocument::_decode_buffer_view(Ref<GLTFState> state, double *dst, const GLTFBufferViewIndex p_buffer_view, const int skip_every, const int skip_bytes, const int element_size, const int count, const GLTFType type, const int component_count, const int component_type, const int component_size, const bool normalized, const int byte_offset, const bool for_vertex) { + const Ref<GLTFBufferView> bv = state->buffer_views[p_buffer_view]; + + int stride = element_size; + if (bv->byte_stride != -1) { + stride = bv->byte_stride; + } + if (for_vertex && stride % 4) { + stride += 4 - (stride % 4); //according to spec must be multiple of 4 + } + + ERR_FAIL_INDEX_V(bv->buffer, state->buffers.size(), ERR_PARSE_ERROR); + + const uint32_t offset = bv->byte_offset + byte_offset; + Vector<uint8_t> buffer = state->buffers[bv->buffer]; //copy on write, so no performance hit + const uint8_t *bufptr = buffer.ptr(); + + //use to debug + print_verbose("glTF: type " + _get_type_name(type) + " component type: " + _get_component_type_name(component_type) + " stride: " + itos(stride) + " amount " + itos(count)); + print_verbose("glTF: accessor offset " + itos(byte_offset) + " view offset: " + itos(bv->byte_offset) + " total buffer len: " + itos(buffer.size()) + " view len " + itos(bv->byte_length)); + + const int buffer_end = (stride * (count - 1)) + element_size; + ERR_FAIL_COND_V(buffer_end > bv->byte_length, ERR_PARSE_ERROR); + + ERR_FAIL_COND_V((int)(offset + buffer_end) > buffer.size(), ERR_PARSE_ERROR); + + //fill everything as doubles + + for (int i = 0; i < count; i++) { + const uint8_t *src = &bufptr[offset + i * stride]; + + for (int j = 0; j < component_count; j++) { + if (skip_every && j > 0 && (j % skip_every) == 0) { + src += skip_bytes; + } + + double d = 0; + + switch (component_type) { + case COMPONENT_TYPE_BYTE: { + int8_t b = int8_t(*src); + if (normalized) { + d = (double(b) / 128.0); + } else { + d = double(b); + } + } break; + case COMPONENT_TYPE_UNSIGNED_BYTE: { + uint8_t b = *src; + if (normalized) { + d = (double(b) / 255.0); + } else { + d = double(b); + } + } break; + case COMPONENT_TYPE_SHORT: { + int16_t s = *(int16_t *)src; + if (normalized) { + d = (double(s) / 32768.0); + } else { + d = double(s); + } + } break; + case COMPONENT_TYPE_UNSIGNED_SHORT: { + uint16_t s = *(uint16_t *)src; + if (normalized) { + d = (double(s) / 65535.0); + } else { + d = double(s); + } + } break; + case COMPONENT_TYPE_INT: { + d = *(int *)src; + } break; + case COMPONENT_TYPE_FLOAT: { + d = *(float *)src; + } break; + } + + *dst++ = d; + src += component_size; + } + } + + return OK; +} + +int GLTFDocument::_get_component_type_size(const int component_type) { + switch (component_type) { + case COMPONENT_TYPE_BYTE: + case COMPONENT_TYPE_UNSIGNED_BYTE: + return 1; + break; + case COMPONENT_TYPE_SHORT: + case COMPONENT_TYPE_UNSIGNED_SHORT: + return 2; + break; + case COMPONENT_TYPE_INT: + case COMPONENT_TYPE_FLOAT: + return 4; + break; + default: { + ERR_FAIL_V(0); + } + } + return 0; +} + +Vector<double> GLTFDocument::_decode_accessor(Ref<GLTFState> state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) { + //spec, for reference: + //https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#data-alignment + + ERR_FAIL_INDEX_V(p_accessor, state->accessors.size(), Vector<double>()); + + const Ref<GLTFAccessor> a = state->accessors[p_accessor]; + + const int component_count_for_type[7] = { + 1, 2, 3, 4, 4, 9, 16 + }; + + const int component_count = component_count_for_type[a->type]; + const int component_size = _get_component_type_size(a->component_type); + ERR_FAIL_COND_V(component_size == 0, Vector<double>()); + int element_size = component_count * component_size; + + int skip_every = 0; + int skip_bytes = 0; + //special case of alignments, as described in spec + switch (a->component_type) { + case COMPONENT_TYPE_BYTE: + case COMPONENT_TYPE_UNSIGNED_BYTE: { + if (a->type == TYPE_MAT2) { + skip_every = 2; + skip_bytes = 2; + element_size = 8; //override for this case + } + if (a->type == TYPE_MAT3) { + skip_every = 3; + skip_bytes = 1; + element_size = 12; //override for this case + } + } break; + case COMPONENT_TYPE_SHORT: + case COMPONENT_TYPE_UNSIGNED_SHORT: { + if (a->type == TYPE_MAT3) { + skip_every = 6; + skip_bytes = 4; + element_size = 16; //override for this case + } + } break; + default: { + } + } + + Vector<double> dst_buffer; + dst_buffer.resize(component_count * a->count); + double *dst = dst_buffer.ptrw(); + + if (a->buffer_view >= 0) { + ERR_FAIL_INDEX_V(a->buffer_view, state->buffer_views.size(), Vector<double>()); + + const Error err = _decode_buffer_view(state, dst, a->buffer_view, skip_every, skip_bytes, element_size, a->count, a->type, component_count, a->component_type, component_size, a->normalized, a->byte_offset, p_for_vertex); + if (err != OK) + return Vector<double>(); + } else { + //fill with zeros, as bufferview is not defined. + for (int i = 0; i < (a->count * component_count); i++) { + dst_buffer.write[i] = 0; + } + } + + if (a->sparse_count > 0) { + // I could not find any file using this, so this code is so far untested + Vector<double> indices; + indices.resize(a->sparse_count); + const int indices_component_size = _get_component_type_size(a->sparse_indices_component_type); + + Error err = _decode_buffer_view(state, indices.ptrw(), a->sparse_indices_buffer_view, 0, 0, indices_component_size, a->sparse_count, TYPE_SCALAR, 1, a->sparse_indices_component_type, indices_component_size, false, a->sparse_indices_byte_offset, false); + if (err != OK) + return Vector<double>(); + + Vector<double> data; + data.resize(component_count * a->sparse_count); + err = _decode_buffer_view(state, data.ptrw(), a->sparse_values_buffer_view, skip_every, skip_bytes, element_size, a->sparse_count, a->type, component_count, a->component_type, component_size, a->normalized, a->sparse_values_byte_offset, p_for_vertex); + if (err != OK) + return Vector<double>(); + + for (int i = 0; i < indices.size(); i++) { + const int write_offset = int(indices[i]) * component_count; + + for (int j = 0; j < component_count; j++) { + dst[write_offset + j] = data[i * component_count + j]; + } + } + } + + return dst_buffer; +} + +GLTFAccessorIndex GLTFDocument::_encode_accessor_as_ints(Ref<GLTFState> state, const Vector<int32_t> p_attribs, const bool p_for_vertex) { + if (p_attribs.size() == 0) { + return -1; + } + const int element_count = 1; + const int ret_size = p_attribs.size(); + Vector<double> attribs; + attribs.resize(ret_size); + Vector<double> type_max; + type_max.resize(element_count); + Vector<double> type_min; + type_min.resize(element_count); + for (int i = 0; i < p_attribs.size(); i++) { + attribs.write[i] = Math::snapped(p_attribs[i], 1.0); + if (i == 0) { + for (int32_t type_i = 0; type_i < element_count; type_i++) { + type_max.write[type_i] = attribs[(i * element_count) + type_i]; + type_min.write[type_i] = attribs[(i * element_count) + type_i]; + } + } + for (int32_t type_i = 0; type_i < element_count; type_i++) { + type_max.write[type_i] = MAX(attribs[(i * element_count) + type_i], type_max[type_i]); + type_min.write[type_i] = MIN(attribs[(i * element_count) + type_i], type_min[type_i]); + type_max.write[type_i] = _filter_number(type_max.write[type_i]); + type_min.write[type_i] = _filter_number(type_min.write[type_i]); + } + } + + ERR_FAIL_COND_V(attribs.size() == 0, -1); + + Ref<GLTFAccessor> accessor; + accessor.instance(); + GLTFBufferIndex buffer_view_i; + int64_t size = state->buffers[0].size(); + const GLTFDocument::GLTFType type = GLTFDocument::TYPE_SCALAR; + const int component_type = GLTFDocument::COMPONENT_TYPE_INT; + + accessor->max = type_max; + accessor->min = type_min; + accessor->normalized = false; + accessor->count = ret_size; + accessor->type = type; + accessor->component_type = component_type; + accessor->byte_offset = 0; + Error err = _encode_buffer_view(state, attribs.ptr(), attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); + if (err != OK) { + return -1; + } + accessor->buffer_view = buffer_view_i; + state->accessors.push_back(accessor); + return state->accessors.size() - 1; +} + +Vector<int> GLTFDocument::_decode_accessor_as_ints(Ref<GLTFState> state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) { + const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex); + Vector<int> ret; + + if (attribs.size() == 0) + return ret; + + const double *attribs_ptr = attribs.ptr(); + const int ret_size = attribs.size(); + ret.resize(ret_size); + { + for (int i = 0; i < ret_size; i++) { + ret.write[i] = int(attribs_ptr[i]); + } + } + return ret; +} + +Vector<float> GLTFDocument::_decode_accessor_as_floats(Ref<GLTFState> state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) { + const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex); + Vector<float> ret; + + if (attribs.size() == 0) + return ret; + + const double *attribs_ptr = attribs.ptr(); + const int ret_size = attribs.size(); + ret.resize(ret_size); + { + for (int i = 0; i < ret_size; i++) { + ret.write[i] = float(attribs_ptr[i]); + } + } + return ret; +} + +GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec2(Ref<GLTFState> state, const Vector<Vector2> p_attribs, const bool p_for_vertex) { + if (p_attribs.size() == 0) { + return -1; + } + const int element_count = 2; + + const int ret_size = p_attribs.size() * element_count; + Vector<double> attribs; + attribs.resize(ret_size); + Vector<double> type_max; + type_max.resize(element_count); + Vector<double> type_min; + type_min.resize(element_count); + + for (int i = 0; i < p_attribs.size(); i++) { + Vector2 attrib = p_attribs[i]; + attribs.write[(i * element_count) + 0] = Math::snapped(attrib.x, CMP_NORMALIZE_TOLERANCE); + attribs.write[(i * element_count) + 1] = Math::snapped(attrib.y, CMP_NORMALIZE_TOLERANCE); + _calc_accessor_min_max(i, element_count, type_max, attribs, type_min); + } + + ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1); + + Ref<GLTFAccessor> accessor; + accessor.instance(); + GLTFBufferIndex buffer_view_i; + int64_t size = state->buffers[0].size(); + const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC2; + const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT; + + accessor->max = type_max; + accessor->min = type_min; + accessor->normalized = false; + accessor->count = p_attribs.size(); + accessor->type = type; + accessor->component_type = component_type; + accessor->byte_offset = 0; + Error err = _encode_buffer_view(state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); + if (err != OK) { + return -1; + } + accessor->buffer_view = buffer_view_i; + state->accessors.push_back(accessor); + return state->accessors.size() - 1; +} + +GLTFAccessorIndex GLTFDocument::_encode_accessor_as_color(Ref<GLTFState> state, const Vector<Color> p_attribs, const bool p_for_vertex) { + if (p_attribs.size() == 0) { + return -1; + } + + const int ret_size = p_attribs.size() * 4; + Vector<double> attribs; + attribs.resize(ret_size); + + const int element_count = 4; + Vector<double> type_max; + type_max.resize(element_count); + Vector<double> type_min; + type_min.resize(element_count); + for (int i = 0; i < p_attribs.size(); i++) { + Color attrib = p_attribs[i]; + attribs.write[(i * element_count) + 0] = Math::snapped(attrib.r, CMP_NORMALIZE_TOLERANCE); + attribs.write[(i * element_count) + 1] = Math::snapped(attrib.g, CMP_NORMALIZE_TOLERANCE); + attribs.write[(i * element_count) + 2] = Math::snapped(attrib.b, CMP_NORMALIZE_TOLERANCE); + attribs.write[(i * element_count) + 3] = Math::snapped(attrib.a, CMP_NORMALIZE_TOLERANCE); + + _calc_accessor_min_max(i, element_count, type_max, attribs, type_min); + } + + ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1); + + Ref<GLTFAccessor> accessor; + accessor.instance(); + GLTFBufferIndex buffer_view_i; + int64_t size = state->buffers[0].size(); + const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC4; + const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT; + + accessor->max = type_max; + accessor->min = type_min; + accessor->normalized = false; + accessor->count = p_attribs.size(); + accessor->type = type; + accessor->component_type = component_type; + accessor->byte_offset = 0; + Error err = _encode_buffer_view(state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); + if (err != OK) { + return -1; + } + accessor->buffer_view = buffer_view_i; + state->accessors.push_back(accessor); + return state->accessors.size() - 1; +} + +void GLTFDocument::_calc_accessor_min_max(int i, const int element_count, Vector<double> &type_max, Vector<double> attribs, Vector<double> &type_min) { + if (i == 0) { + for (int32_t type_i = 0; type_i < element_count; type_i++) { + type_max.write[type_i] = attribs[(i * element_count) + type_i]; + type_min.write[type_i] = attribs[(i * element_count) + type_i]; + } + } + for (int32_t type_i = 0; type_i < element_count; type_i++) { + type_max.write[type_i] = MAX(attribs[(i * element_count) + type_i], type_max[type_i]); + type_min.write[type_i] = MIN(attribs[(i * element_count) + type_i], type_min[type_i]); + type_max.write[type_i] = _filter_number(type_max.write[type_i]); + type_min.write[type_i] = _filter_number(type_min.write[type_i]); + } +} + +GLTFAccessorIndex GLTFDocument::_encode_accessor_as_weights(Ref<GLTFState> state, const Vector<Color> p_attribs, const bool p_for_vertex) { + if (p_attribs.size() == 0) { + return -1; + } + + const int ret_size = p_attribs.size() * 4; + Vector<double> attribs; + attribs.resize(ret_size); + + const int element_count = 4; + + Vector<double> type_max; + type_max.resize(element_count); + Vector<double> type_min; + type_min.resize(element_count); + for (int i = 0; i < p_attribs.size(); i++) { + Color attrib = p_attribs[i]; + attribs.write[(i * element_count) + 0] = Math::snapped(attrib.r, CMP_NORMALIZE_TOLERANCE); + attribs.write[(i * element_count) + 1] = Math::snapped(attrib.g, CMP_NORMALIZE_TOLERANCE); + attribs.write[(i * element_count) + 2] = Math::snapped(attrib.b, CMP_NORMALIZE_TOLERANCE); + attribs.write[(i * element_count) + 3] = Math::snapped(attrib.a, CMP_NORMALIZE_TOLERANCE); + + _calc_accessor_min_max(i, element_count, type_max, attribs, type_min); + } + + ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1); + + Ref<GLTFAccessor> accessor; + accessor.instance(); + GLTFBufferIndex buffer_view_i; + int64_t size = state->buffers[0].size(); + const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC4; + const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT; + + accessor->max = type_max; + accessor->min = type_min; + accessor->normalized = false; + accessor->count = p_attribs.size(); + accessor->type = type; + accessor->component_type = component_type; + accessor->byte_offset = 0; + Error err = _encode_buffer_view(state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); + if (err != OK) { + return -1; + } + accessor->buffer_view = buffer_view_i; + state->accessors.push_back(accessor); + return state->accessors.size() - 1; +} + +GLTFAccessorIndex GLTFDocument::_encode_accessor_as_joints(Ref<GLTFState> state, const Vector<Color> p_attribs, const bool p_for_vertex) { + if (p_attribs.size() == 0) { + return -1; + } + + const int element_count = 4; + const int ret_size = p_attribs.size() * element_count; + Vector<double> attribs; + attribs.resize(ret_size); + + Vector<double> type_max; + type_max.resize(element_count); + Vector<double> type_min; + type_min.resize(element_count); + for (int i = 0; i < p_attribs.size(); i++) { + Color attrib = p_attribs[i]; + attribs.write[(i * element_count) + 0] = Math::snapped(attrib.r, CMP_NORMALIZE_TOLERANCE); + attribs.write[(i * element_count) + 1] = Math::snapped(attrib.g, CMP_NORMALIZE_TOLERANCE); + attribs.write[(i * element_count) + 2] = Math::snapped(attrib.b, CMP_NORMALIZE_TOLERANCE); + attribs.write[(i * element_count) + 3] = Math::snapped(attrib.a, CMP_NORMALIZE_TOLERANCE); + _calc_accessor_min_max(i, element_count, type_max, attribs, type_min); + } + ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1); + + Ref<GLTFAccessor> accessor; + accessor.instance(); + GLTFBufferIndex buffer_view_i; + int64_t size = state->buffers[0].size(); + const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC4; + const int component_type = GLTFDocument::COMPONENT_TYPE_UNSIGNED_SHORT; + + accessor->max = type_max; + accessor->min = type_min; + accessor->normalized = false; + accessor->count = p_attribs.size(); + accessor->type = type; + accessor->component_type = component_type; + accessor->byte_offset = 0; + Error err = _encode_buffer_view(state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); + if (err != OK) { + return -1; + } + accessor->buffer_view = buffer_view_i; + state->accessors.push_back(accessor); + return state->accessors.size() - 1; +} + +GLTFAccessorIndex GLTFDocument::_encode_accessor_as_quats(Ref<GLTFState> state, const Vector<Quat> p_attribs, const bool p_for_vertex) { + if (p_attribs.size() == 0) { + return -1; + } + const int element_count = 4; + + const int ret_size = p_attribs.size() * element_count; + Vector<double> attribs; + attribs.resize(ret_size); + + Vector<double> type_max; + type_max.resize(element_count); + Vector<double> type_min; + type_min.resize(element_count); + for (int i = 0; i < p_attribs.size(); i++) { + Quat quat = p_attribs[i]; + attribs.write[(i * element_count) + 0] = Math::snapped(quat.x, CMP_NORMALIZE_TOLERANCE); + attribs.write[(i * element_count) + 1] = Math::snapped(quat.y, CMP_NORMALIZE_TOLERANCE); + attribs.write[(i * element_count) + 2] = Math::snapped(quat.z, CMP_NORMALIZE_TOLERANCE); + attribs.write[(i * element_count) + 3] = Math::snapped(quat.w, CMP_NORMALIZE_TOLERANCE); + + _calc_accessor_min_max(i, element_count, type_max, attribs, type_min); + } + + ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1); + + Ref<GLTFAccessor> accessor; + accessor.instance(); + GLTFBufferIndex buffer_view_i; + int64_t size = state->buffers[0].size(); + const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC4; + const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT; + + accessor->max = type_max; + accessor->min = type_min; + accessor->normalized = false; + accessor->count = p_attribs.size(); + accessor->type = type; + accessor->component_type = component_type; + accessor->byte_offset = 0; + Error err = _encode_buffer_view(state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); + if (err != OK) { + return -1; + } + accessor->buffer_view = buffer_view_i; + state->accessors.push_back(accessor); + return state->accessors.size() - 1; +} + +Vector<Vector2> GLTFDocument::_decode_accessor_as_vec2(Ref<GLTFState> state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) { + const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex); + Vector<Vector2> ret; + + if (attribs.size() == 0) + return ret; + + ERR_FAIL_COND_V(attribs.size() % 2 != 0, ret); + const double *attribs_ptr = attribs.ptr(); + const int ret_size = attribs.size() / 2; + ret.resize(ret_size); + { + for (int i = 0; i < ret_size; i++) { + ret.write[i] = Vector2(attribs_ptr[i * 2 + 0], attribs_ptr[i * 2 + 1]); + } + } + return ret; +} + +GLTFAccessorIndex GLTFDocument::_encode_accessor_as_floats(Ref<GLTFState> state, const Vector<real_t> p_attribs, const bool p_for_vertex) { + if (p_attribs.size() == 0) { + return -1; + } + const int element_count = 1; + const int ret_size = p_attribs.size(); + Vector<double> attribs; + attribs.resize(ret_size); + + Vector<double> type_max; + type_max.resize(element_count); + Vector<double> type_min; + type_min.resize(element_count); + + for (int i = 0; i < p_attribs.size(); i++) { + attribs.write[i] = Math::snapped(p_attribs[i], CMP_NORMALIZE_TOLERANCE); + + _calc_accessor_min_max(i, element_count, type_max, attribs, type_min); + } + + ERR_FAIL_COND_V(!attribs.size(), -1); + + Ref<GLTFAccessor> accessor; + accessor.instance(); + GLTFBufferIndex buffer_view_i; + int64_t size = state->buffers[0].size(); + const GLTFDocument::GLTFType type = GLTFDocument::TYPE_SCALAR; + const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT; + + accessor->max = type_max; + accessor->min = type_min; + accessor->normalized = false; + accessor->count = ret_size; + accessor->type = type; + accessor->component_type = component_type; + accessor->byte_offset = 0; + Error err = _encode_buffer_view(state, attribs.ptr(), attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); + if (err != OK) { + return -1; + } + accessor->buffer_view = buffer_view_i; + state->accessors.push_back(accessor); + return state->accessors.size() - 1; +} + +GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec3(Ref<GLTFState> state, const Vector<Vector3> p_attribs, const bool p_for_vertex) { + if (p_attribs.size() == 0) { + return -1; + } + const int element_count = 3; + const int ret_size = p_attribs.size() * element_count; + Vector<double> attribs; + attribs.resize(ret_size); + + Vector<double> type_max; + type_max.resize(element_count); + Vector<double> type_min; + type_min.resize(element_count); + for (int i = 0; i < p_attribs.size(); i++) { + Vector3 attrib = p_attribs[i]; + attribs.write[(i * element_count) + 0] = Math::snapped(attrib.x, CMP_NORMALIZE_TOLERANCE); + attribs.write[(i * element_count) + 1] = Math::snapped(attrib.y, CMP_NORMALIZE_TOLERANCE); + attribs.write[(i * element_count) + 2] = Math::snapped(attrib.z, CMP_NORMALIZE_TOLERANCE); + + _calc_accessor_min_max(i, element_count, type_max, attribs, type_min); + } + ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1); + + Ref<GLTFAccessor> accessor; + accessor.instance(); + GLTFBufferIndex buffer_view_i; + int64_t size = state->buffers[0].size(); + const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC3; + const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT; + + accessor->max = type_max; + accessor->min = type_min; + accessor->normalized = false; + accessor->count = p_attribs.size(); + accessor->type = type; + accessor->component_type = component_type; + accessor->byte_offset = 0; + Error err = _encode_buffer_view(state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); + if (err != OK) { + return -1; + } + accessor->buffer_view = buffer_view_i; + state->accessors.push_back(accessor); + return state->accessors.size() - 1; +} + +GLTFAccessorIndex GLTFDocument::_encode_accessor_as_xform(Ref<GLTFState> state, const Vector<Transform> p_attribs, const bool p_for_vertex) { + if (p_attribs.size() == 0) { + return -1; + } + const int element_count = 16; + const int ret_size = p_attribs.size() * element_count; + Vector<double> attribs; + attribs.resize(ret_size); + + Vector<double> type_max; + type_max.resize(element_count); + Vector<double> type_min; + type_min.resize(element_count); + for (int i = 0; i < p_attribs.size(); i++) { + Transform attrib = p_attribs[i]; + Basis basis = attrib.get_basis(); + Vector3 axis_0 = basis.get_axis(Vector3::AXIS_X); + + attribs.write[i * element_count + 0] = Math::snapped(axis_0.x, CMP_NORMALIZE_TOLERANCE); + attribs.write[i * element_count + 1] = Math::snapped(axis_0.y, CMP_NORMALIZE_TOLERANCE); + attribs.write[i * element_count + 2] = Math::snapped(axis_0.z, CMP_NORMALIZE_TOLERANCE); + attribs.write[i * element_count + 3] = 0.0; + + Vector3 axis_1 = basis.get_axis(Vector3::AXIS_Y); + attribs.write[i * element_count + 4] = Math::snapped(axis_1.x, CMP_NORMALIZE_TOLERANCE); + attribs.write[i * element_count + 5] = Math::snapped(axis_1.y, CMP_NORMALIZE_TOLERANCE); + attribs.write[i * element_count + 6] = Math::snapped(axis_1.z, CMP_NORMALIZE_TOLERANCE); + attribs.write[i * element_count + 7] = 0.0; + + Vector3 axis_2 = basis.get_axis(Vector3::AXIS_Z); + attribs.write[i * element_count + 8] = Math::snapped(axis_2.x, CMP_NORMALIZE_TOLERANCE); + attribs.write[i * element_count + 9] = Math::snapped(axis_2.y, CMP_NORMALIZE_TOLERANCE); + attribs.write[i * element_count + 10] = Math::snapped(axis_2.z, CMP_NORMALIZE_TOLERANCE); + attribs.write[i * element_count + 11] = 0.0; + + Vector3 origin = attrib.get_origin(); + attribs.write[i * element_count + 12] = Math::snapped(origin.x, CMP_NORMALIZE_TOLERANCE); + attribs.write[i * element_count + 13] = Math::snapped(origin.y, CMP_NORMALIZE_TOLERANCE); + attribs.write[i * element_count + 14] = Math::snapped(origin.z, CMP_NORMALIZE_TOLERANCE); + attribs.write[i * element_count + 15] = 1.0; + + _calc_accessor_min_max(i, element_count, type_max, attribs, type_min); + } + ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1); + + Ref<GLTFAccessor> accessor; + accessor.instance(); + GLTFBufferIndex buffer_view_i; + int64_t size = state->buffers[0].size(); + const GLTFDocument::GLTFType type = GLTFDocument::TYPE_MAT4; + const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT; + + accessor->max = type_max; + accessor->min = type_min; + accessor->normalized = false; + accessor->count = p_attribs.size(); + accessor->type = type; + accessor->component_type = component_type; + accessor->byte_offset = 0; + Error err = _encode_buffer_view(state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); + if (err != OK) { + return -1; + } + accessor->buffer_view = buffer_view_i; + state->accessors.push_back(accessor); + return state->accessors.size() - 1; +} + +Vector<Vector3> GLTFDocument::_decode_accessor_as_vec3(Ref<GLTFState> state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) { + const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex); + Vector<Vector3> ret; + + if (attribs.size() == 0) + return ret; + + ERR_FAIL_COND_V(attribs.size() % 3 != 0, ret); + const double *attribs_ptr = attribs.ptr(); + const int ret_size = attribs.size() / 3; + ret.resize(ret_size); + { + for (int i = 0; i < ret_size; i++) { + ret.write[i] = Vector3(attribs_ptr[i * 3 + 0], attribs_ptr[i * 3 + 1], attribs_ptr[i * 3 + 2]); + } + } + return ret; +} + +Vector<Color> GLTFDocument::_decode_accessor_as_color(Ref<GLTFState> state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) { + const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex); + Vector<Color> ret; + + if (attribs.size() == 0) + return ret; + + const int type = state->accessors[p_accessor]->type; + ERR_FAIL_COND_V(!(type == TYPE_VEC3 || type == TYPE_VEC4), ret); + int vec_len = 3; + if (type == TYPE_VEC4) { + vec_len = 4; + } + + ERR_FAIL_COND_V(attribs.size() % vec_len != 0, ret); + const double *attribs_ptr = attribs.ptr(); + const int ret_size = attribs.size() / vec_len; + ret.resize(ret_size); + { + for (int i = 0; i < ret_size; i++) { + ret.write[i] = Color(attribs_ptr[i * vec_len + 0], attribs_ptr[i * vec_len + 1], attribs_ptr[i * vec_len + 2], vec_len == 4 ? attribs_ptr[i * 4 + 3] : 1.0); + } + } + return ret; +} +Vector<Quat> GLTFDocument::_decode_accessor_as_quat(Ref<GLTFState> state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) { + const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex); + Vector<Quat> ret; + + if (attribs.size() == 0) + return ret; + + ERR_FAIL_COND_V(attribs.size() % 4 != 0, ret); + const double *attribs_ptr = attribs.ptr(); + const int ret_size = attribs.size() / 4; + ret.resize(ret_size); + { + for (int i = 0; i < ret_size; i++) { + ret.write[i] = Quat(attribs_ptr[i * 4 + 0], attribs_ptr[i * 4 + 1], attribs_ptr[i * 4 + 2], attribs_ptr[i * 4 + 3]).normalized(); + } + } + return ret; +} +Vector<Transform2D> GLTFDocument::_decode_accessor_as_xform2d(Ref<GLTFState> state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) { + const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex); + Vector<Transform2D> ret; + + if (attribs.size() == 0) + return ret; + + ERR_FAIL_COND_V(attribs.size() % 4 != 0, ret); + ret.resize(attribs.size() / 4); + for (int i = 0; i < ret.size(); i++) { + ret.write[i][0] = Vector2(attribs[i * 4 + 0], attribs[i * 4 + 1]); + ret.write[i][1] = Vector2(attribs[i * 4 + 2], attribs[i * 4 + 3]); + } + return ret; +} + +Vector<Basis> GLTFDocument::_decode_accessor_as_basis(Ref<GLTFState> state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) { + const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex); + Vector<Basis> ret; + + if (attribs.size() == 0) + return ret; + + ERR_FAIL_COND_V(attribs.size() % 9 != 0, ret); + ret.resize(attribs.size() / 9); + for (int i = 0; i < ret.size(); i++) { + ret.write[i].set_axis(0, Vector3(attribs[i * 9 + 0], attribs[i * 9 + 1], attribs[i * 9 + 2])); + ret.write[i].set_axis(1, Vector3(attribs[i * 9 + 3], attribs[i * 9 + 4], attribs[i * 9 + 5])); + ret.write[i].set_axis(2, Vector3(attribs[i * 9 + 6], attribs[i * 9 + 7], attribs[i * 9 + 8])); + } + return ret; +} + +Vector<Transform> GLTFDocument::_decode_accessor_as_xform(Ref<GLTFState> state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) { + const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex); + Vector<Transform> ret; + + if (attribs.size() == 0) + return ret; + + ERR_FAIL_COND_V(attribs.size() % 16 != 0, ret); + ret.resize(attribs.size() / 16); + for (int i = 0; i < ret.size(); i++) { + ret.write[i].basis.set_axis(0, Vector3(attribs[i * 16 + 0], attribs[i * 16 + 1], attribs[i * 16 + 2])); + ret.write[i].basis.set_axis(1, Vector3(attribs[i * 16 + 4], attribs[i * 16 + 5], attribs[i * 16 + 6])); + ret.write[i].basis.set_axis(2, Vector3(attribs[i * 16 + 8], attribs[i * 16 + 9], attribs[i * 16 + 10])); + ret.write[i].set_origin(Vector3(attribs[i * 16 + 12], attribs[i * 16 + 13], attribs[i * 16 + 14])); + } + return ret; +} + +Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) { + Array meshes; + for (GLTFMeshIndex gltf_mesh_i = 0; gltf_mesh_i < state->meshes.size(); gltf_mesh_i++) { + print_verbose("glTF: Serializing mesh: " + itos(gltf_mesh_i)); + Ref<EditorSceneImporterMesh> import_mesh = state->meshes.write[gltf_mesh_i]->get_mesh(); + if (import_mesh.is_null()) { + continue; + } + Array primitives; + Array targets; + Dictionary gltf_mesh; + Array target_names; + Array weights; + for (int surface_i = 0; surface_i < import_mesh->get_surface_count(); surface_i++) { + Dictionary primitive; + Mesh::PrimitiveType primitive_type = import_mesh->get_surface_primitive_type(surface_i); + switch (primitive_type) { + case Mesh::PRIMITIVE_POINTS: { + primitive["mode"] = 0; + break; + } + case Mesh::PRIMITIVE_LINES: { + primitive["mode"] = 1; + break; + } + // case Mesh::PRIMITIVE_LINE_LOOP: { + // primitive["mode"] = 2; + // break; + // } + case Mesh::PRIMITIVE_LINE_STRIP: { + primitive["mode"] = 3; + break; + } + case Mesh::PRIMITIVE_TRIANGLES: { + primitive["mode"] = 4; + break; + } + case Mesh::PRIMITIVE_TRIANGLE_STRIP: { + primitive["mode"] = 5; + break; + } + // case Mesh::PRIMITIVE_TRIANGLE_FAN: { + // primitive["mode"] = 6; + // break; + // } + default: { + ERR_FAIL_V(FAILED); + } + } + + Array array = import_mesh->get_surface_arrays(surface_i); + Dictionary attributes; + { + Vector<Vector3> a = array[Mesh::ARRAY_VERTEX]; + ERR_FAIL_COND_V(!a.size(), ERR_INVALID_DATA); + attributes["POSITION"] = _encode_accessor_as_vec3(state, a, true); + } + { + Vector<real_t> a = array[Mesh::ARRAY_TANGENT]; + if (a.size()) { + const int ret_size = a.size() / 4; + Vector<Color> attribs; + attribs.resize(ret_size); + for (int i = 0; i < ret_size; i++) { + Color out; + out.r = a[(i * 4) + 0]; + out.g = a[(i * 4) + 1]; + out.b = a[(i * 4) + 2]; + out.a = a[(i * 4) + 3]; + attribs.write[i] = out; + } + attributes["TANGENT"] = _encode_accessor_as_color(state, attribs, true); + } + } + { + Vector<Vector3> a = array[Mesh::ARRAY_NORMAL]; + if (a.size()) { + const int ret_size = a.size(); + Vector<Vector3> attribs; + attribs.resize(ret_size); + for (int i = 0; i < ret_size; i++) { + attribs.write[i] = Vector3(a[i]).normalized(); + } + attributes["NORMAL"] = _encode_accessor_as_vec3(state, attribs, true); + } + } + { + Vector<Vector2> a = array[Mesh::ARRAY_TEX_UV]; + if (a.size()) { + attributes["TEXCOORD_0"] = _encode_accessor_as_vec2(state, a, true); + } + } + { + Vector<Vector2> a = array[Mesh::ARRAY_TEX_UV2]; + if (a.size()) { + attributes["TEXCOORD_1"] = _encode_accessor_as_vec2(state, a, true); + } + } + { + Vector<Color> a = array[Mesh::ARRAY_COLOR]; + if (a.size()) { + attributes["COLOR_0"] = _encode_accessor_as_color(state, a, true); + } + } + Map<int, int> joint_i_to_bone_i; + for (GLTFNodeIndex node_i = 0; node_i < state->nodes.size(); node_i++) { + GLTFSkinIndex skin_i = -1; + if (state->nodes[node_i]->mesh == gltf_mesh_i) { + skin_i = state->nodes[node_i]->skin; + } + if (skin_i != -1) { + joint_i_to_bone_i = state->skins[skin_i]->joint_i_to_bone_i; + break; + } + } + { + const Array &a = array[Mesh::ARRAY_BONES]; + const Vector<Vector3> &vertex_array = array[Mesh::ARRAY_VERTEX]; + if ((a.size() / JOINT_GROUP_SIZE) == vertex_array.size()) { + const int ret_size = a.size() / JOINT_GROUP_SIZE; + Vector<Color> attribs; + attribs.resize(ret_size); + { + for (int array_i = 0; array_i < attribs.size(); array_i++) { + int32_t joint_0 = a[(array_i * JOINT_GROUP_SIZE) + 0]; + int32_t joint_1 = a[(array_i * JOINT_GROUP_SIZE) + 1]; + int32_t joint_2 = a[(array_i * JOINT_GROUP_SIZE) + 2]; + int32_t joint_3 = a[(array_i * JOINT_GROUP_SIZE) + 3]; + attribs.write[array_i] = Color(joint_0, joint_1, joint_2, joint_3); + } + } + attributes["JOINTS_0"] = _encode_accessor_as_joints(state, attribs, true); + } else if ((a.size() / (JOINT_GROUP_SIZE * 2)) >= vertex_array.size()) { + int32_t vertex_count = vertex_array.size(); + Vector<Color> joints_0; + joints_0.resize(vertex_count); + Vector<Color> joints_1; + joints_1.resize(vertex_count); + int32_t weights_8_count = JOINT_GROUP_SIZE * 2; + for (int32_t vertex_i = 0; vertex_i < vertex_count; vertex_i++) { + Color joint_0; + joint_0.r = a[vertex_i * weights_8_count + 0]; + joint_0.g = a[vertex_i * weights_8_count + 1]; + joint_0.b = a[vertex_i * weights_8_count + 2]; + joint_0.a = a[vertex_i * weights_8_count + 3]; + joints_0.write[vertex_i] = joint_0; + Color joint_1; + joint_1.r = a[vertex_i * weights_8_count + 4]; + joint_1.g = a[vertex_i * weights_8_count + 5]; + joint_1.b = a[vertex_i * weights_8_count + 6]; + joint_1.a = a[vertex_i * weights_8_count + 7]; + joints_1.write[vertex_i] = joint_1; + } + attributes["JOINTS_0"] = _encode_accessor_as_joints(state, joints_0, true); + attributes["JOINTS_1"] = _encode_accessor_as_joints(state, joints_1, true); + } + } + { + const Array &a = array[Mesh::ARRAY_WEIGHTS]; + const Vector<Vector3> &vertex_array = array[Mesh::ARRAY_VERTEX]; + if ((a.size() / JOINT_GROUP_SIZE) == vertex_array.size()) { + const int ret_size = a.size() / JOINT_GROUP_SIZE; + Vector<Color> attribs; + attribs.resize(ret_size); + for (int i = 0; i < ret_size; i++) { + attribs.write[i] = Color(a[(i * JOINT_GROUP_SIZE) + 0], a[(i * JOINT_GROUP_SIZE) + 1], a[(i * JOINT_GROUP_SIZE) + 2], a[(i * JOINT_GROUP_SIZE) + 3]); + } + attributes["WEIGHTS_0"] = _encode_accessor_as_weights(state, attribs, true); + } else if ((a.size() / (JOINT_GROUP_SIZE * 2)) >= vertex_array.size()) { + int32_t vertex_count = vertex_array.size(); + Vector<Color> weights_0; + weights_0.resize(vertex_count); + Vector<Color> weights_1; + weights_1.resize(vertex_count); + int32_t weights_8_count = JOINT_GROUP_SIZE * 2; + for (int32_t vertex_i = 0; vertex_i < vertex_count; vertex_i++) { + Color weight_0; + weight_0.r = a[vertex_i * weights_8_count + 0]; + weight_0.g = a[vertex_i * weights_8_count + 1]; + weight_0.b = a[vertex_i * weights_8_count + 2]; + weight_0.a = a[vertex_i * weights_8_count + 3]; + weights_0.write[vertex_i] = weight_0; + Color weight_1; + weight_1.r = a[vertex_i * weights_8_count + 4]; + weight_1.g = a[vertex_i * weights_8_count + 5]; + weight_1.b = a[vertex_i * weights_8_count + 6]; + weight_1.a = a[vertex_i * weights_8_count + 7]; + weights_1.write[vertex_i] = weight_1; + } + attributes["WEIGHTS_0"] = _encode_accessor_as_weights(state, weights_0, true); + attributes["WEIGHTS_1"] = _encode_accessor_as_weights(state, weights_1, true); + } + } + { + Vector<int32_t> mesh_indices = array[Mesh::ARRAY_INDEX]; + if (mesh_indices.size()) { + if (primitive_type == Mesh::PRIMITIVE_TRIANGLES) { + //swap around indices, convert ccw to cw for front face + const int is = mesh_indices.size(); + for (int k = 0; k < is; k += 3) { + SWAP(mesh_indices.write[k + 0], mesh_indices.write[k + 2]); + } + } + primitive["indices"] = _encode_accessor_as_ints(state, mesh_indices, true); + } else { + if (primitive_type == Mesh::PRIMITIVE_TRIANGLES) { + //generate indices because they need to be swapped for CW/CCW + const Vector<Vector3> &vertices = array[Mesh::ARRAY_VERTEX]; + Ref<SurfaceTool> st; + st.instance(); + st->create_from_triangle_arrays(array); + st->index(); + Vector<int32_t> generated_indices = st->commit_to_arrays()[Mesh::ARRAY_INDEX]; + const int vs = vertices.size(); + generated_indices.resize(vs); + { + for (int k = 0; k < vs; k += 3) { + generated_indices.write[k] = k; + generated_indices.write[k + 1] = k + 2; + generated_indices.write[k + 2] = k + 1; + } + } + primitive["indices"] = _encode_accessor_as_ints(state, generated_indices, true); + } + } + } + + primitive["attributes"] = attributes; + + //blend shapes + print_verbose("glTF: Mesh has targets"); + if (import_mesh->get_blend_shape_count()) { + ArrayMesh::BlendShapeMode shape_mode = import_mesh->get_blend_shape_mode(); + for (int morph_i = 0; morph_i < import_mesh->get_blend_shape_count(); morph_i++) { + Array array_morph = import_mesh->get_surface_blend_shape_arrays(surface_i, morph_i); + target_names.push_back(import_mesh->get_blend_shape_name(morph_i)); + Dictionary t; + Vector<Vector3> varr = array_morph[Mesh::ARRAY_VERTEX]; + Array mesh_arrays = import_mesh->get_surface_arrays(surface_i); + if (varr.size()) { + Vector<Vector3> src_varr = array[Mesh::ARRAY_VERTEX]; + if (shape_mode == ArrayMesh::BlendShapeMode::BLEND_SHAPE_MODE_NORMALIZED) { + const int max_idx = src_varr.size(); + for (int blend_i = 0; blend_i < max_idx; blend_i++) { + varr.write[blend_i] = Vector3(varr[blend_i]) - src_varr[blend_i]; + } + } + + t["POSITION"] = _encode_accessor_as_vec3(state, varr, true); + } + + Vector<Vector3> narr = array_morph[Mesh::ARRAY_NORMAL]; + if (varr.size()) { + t["NORMAL"] = _encode_accessor_as_vec3(state, narr, true); + } + Vector<real_t> tarr = array_morph[Mesh::ARRAY_TANGENT]; + if (tarr.size()) { + const int ret_size = tarr.size() / 4; + Vector<Color> attribs; + attribs.resize(ret_size); + for (int i = 0; i < ret_size; i++) { + Color tangent; + tangent.r = tarr[(i * 4) + 0]; + tangent.r = tarr[(i * 4) + 1]; + tangent.r = tarr[(i * 4) + 2]; + tangent.r = tarr[(i * 4) + 3]; + } + t["TANGENT"] = _encode_accessor_as_color(state, attribs, true); + } + targets.push_back(t); + } + } + + Ref<BaseMaterial3D> mat = import_mesh->get_surface_material(surface_i); + if (mat.is_valid()) { + Map<Ref<BaseMaterial3D>, GLTFMaterialIndex>::Element *material_cache_i = state->material_cache.find(mat); + if (material_cache_i && material_cache_i->get() != -1) { + primitive["material"] = material_cache_i->get(); + } else { + GLTFMaterialIndex mat_i = state->materials.size(); + state->materials.push_back(mat); + primitive["material"] = mat_i; + state->material_cache.insert(mat, mat_i); + } + } + + if (targets.size()) { + primitive["targets"] = targets; + } + + primitives.push_back(primitive); + } + + Dictionary e; + e["targetNames"] = target_names; + + for (int j = 0; j < target_names.size(); j++) { + real_t weight = 0; + if (j < state->meshes.write[gltf_mesh_i]->get_blend_weights().size()) { + weight = state->meshes.write[gltf_mesh_i]->get_blend_weights()[j]; + } + weights.push_back(weight); + } + if (weights.size()) { + gltf_mesh["weights"] = weights; + } + + ERR_FAIL_COND_V(target_names.size() != weights.size(), FAILED); + + gltf_mesh["extras"] = e; + + gltf_mesh["primitives"] = primitives; + + meshes.push_back(gltf_mesh); + } + + state->json["meshes"] = meshes; + print_verbose("glTF: Total meshes: " + itos(meshes.size())); + + return OK; +} + +Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) { + if (!state->json.has("meshes")) { + return OK; + } + + Array meshes = state->json["meshes"]; + for (GLTFMeshIndex i = 0; i < meshes.size(); i++) { + print_verbose("glTF: Parsing mesh: " + itos(i)); + Dictionary d = meshes[i]; + + Ref<GLTFMesh> mesh; + mesh.instance(); + bool has_vertex_color = false; + + ERR_FAIL_COND_V(!d.has("primitives"), ERR_PARSE_ERROR); + + Array primitives = d["primitives"]; + const Dictionary &extras = d.has("extras") ? (Dictionary)d["extras"] : Dictionary(); + Ref<EditorSceneImporterMesh> import_mesh; + import_mesh.instance(); + for (int j = 0; j < primitives.size(); j++) { + Dictionary p = primitives[j]; + + Array array; + array.resize(Mesh::ARRAY_MAX); + + ERR_FAIL_COND_V(!p.has("attributes"), ERR_PARSE_ERROR); + + Dictionary a = p["attributes"]; + + Mesh::PrimitiveType primitive = Mesh::PRIMITIVE_TRIANGLES; + if (p.has("mode")) { + const int mode = p["mode"]; + ERR_FAIL_INDEX_V(mode, 7, ERR_FILE_CORRUPT); + static const Mesh::PrimitiveType primitives2[7] = { + Mesh::PRIMITIVE_POINTS, + Mesh::PRIMITIVE_LINES, + Mesh::PRIMITIVE_LINES, //loop not supported, should ce converted + Mesh::PRIMITIVE_LINES, + Mesh::PRIMITIVE_TRIANGLES, + Mesh::PRIMITIVE_TRIANGLE_STRIP, + Mesh::PRIMITIVE_TRIANGLES, //fan not supported, should be converted +#ifndef _MSC_VER +#warning line loop and triangle fan are not supported and need to be converted to lines and triangles +#endif + + }; + + primitive = primitives2[mode]; + } + + ERR_FAIL_COND_V(!a.has("POSITION"), ERR_PARSE_ERROR); + if (a.has("POSITION")) { + array[Mesh::ARRAY_VERTEX] = _decode_accessor_as_vec3(state, a["POSITION"], true); + } + if (a.has("NORMAL")) { + array[Mesh::ARRAY_NORMAL] = _decode_accessor_as_vec3(state, a["NORMAL"], true); + } + if (a.has("TANGENT")) { + array[Mesh::ARRAY_TANGENT] = _decode_accessor_as_floats(state, a["TANGENT"], true); + } + if (a.has("TEXCOORD_0")) { + array[Mesh::ARRAY_TEX_UV] = _decode_accessor_as_vec2(state, a["TEXCOORD_0"], true); + } + if (a.has("TEXCOORD_1")) { + array[Mesh::ARRAY_TEX_UV2] = _decode_accessor_as_vec2(state, a["TEXCOORD_1"], true); + } + if (a.has("COLOR_0")) { + array[Mesh::ARRAY_COLOR] = _decode_accessor_as_color(state, a["COLOR_0"], true); + has_vertex_color = true; + } + if (a.has("JOINTS_0") && !a.has("JOINTS_1")) { + array[Mesh::ARRAY_BONES] = _decode_accessor_as_ints(state, a["JOINTS_0"], true); + } else if (a.has("JOINTS_0") && a.has("JOINTS_1")) { + PackedInt32Array joints_0 = _decode_accessor_as_ints(state, a["JOINTS_0"], true); + PackedInt32Array joints_1 = _decode_accessor_as_ints(state, a["JOINTS_1"], true); + ERR_FAIL_COND_V(joints_0.size() != joints_0.size(), ERR_INVALID_DATA); + int32_t weight_8_count = JOINT_GROUP_SIZE * 2; + int32_t vertex_count = joints_0.size() / JOINT_GROUP_SIZE; + Vector<int> joints; + joints.resize(vertex_count * weight_8_count); + for (int32_t vertex_i = 0; vertex_i < vertex_count; vertex_i++) { + joints.write[vertex_i * weight_8_count + 0] = joints_0[vertex_i * JOINT_GROUP_SIZE + 0]; + joints.write[vertex_i * weight_8_count + 1] = joints_0[vertex_i * JOINT_GROUP_SIZE + 1]; + joints.write[vertex_i * weight_8_count + 2] = joints_0[vertex_i * JOINT_GROUP_SIZE + 2]; + joints.write[vertex_i * weight_8_count + 3] = joints_0[vertex_i * JOINT_GROUP_SIZE + 3]; + joints.write[vertex_i * weight_8_count + 4] = joints_1[vertex_i * JOINT_GROUP_SIZE + 0]; + joints.write[vertex_i * weight_8_count + 5] = joints_1[vertex_i * JOINT_GROUP_SIZE + 1]; + joints.write[vertex_i * weight_8_count + 6] = joints_1[vertex_i * JOINT_GROUP_SIZE + 2]; + joints.write[vertex_i * weight_8_count + 7] = joints_1[vertex_i * JOINT_GROUP_SIZE + 3]; + } + array[Mesh::ARRAY_BONES] = joints; + } + if (a.has("WEIGHTS_0") && !a.has("WEIGHTS_1")) { + Vector<float> weights = _decode_accessor_as_floats(state, a["WEIGHTS_0"], true); + { //gltf does not seem to normalize the weights for some reason.. + int wc = weights.size(); + float *w = weights.ptrw(); + + for (int k = 0; k < wc; k += 4) { + float total = 0.0; + total += w[k + 0]; + total += w[k + 1]; + total += w[k + 2]; + total += w[k + 3]; + if (total > 0.0) { + w[k + 0] /= total; + w[k + 1] /= total; + w[k + 2] /= total; + w[k + 3] /= total; + } + } + } + array[Mesh::ARRAY_WEIGHTS] = weights; + } else if (a.has("WEIGHTS_0") && a.has("WEIGHTS_1")) { + Vector<float> weights_0 = _decode_accessor_as_floats(state, a["WEIGHTS_0"], true); + Vector<float> weights_1 = _decode_accessor_as_floats(state, a["WEIGHTS_1"], true); + Vector<float> weights; + ERR_FAIL_COND_V(weights_0.size() != weights_1.size(), ERR_INVALID_DATA); + int32_t weight_8_count = JOINT_GROUP_SIZE * 2; + int32_t vertex_count = weights_0.size() / JOINT_GROUP_SIZE; + weights.resize(vertex_count * weight_8_count); + for (int32_t vertex_i = 0; vertex_i < vertex_count; vertex_i++) { + weights.write[vertex_i * weight_8_count + 0] = weights_0[vertex_i * JOINT_GROUP_SIZE + 0]; + weights.write[vertex_i * weight_8_count + 1] = weights_0[vertex_i * JOINT_GROUP_SIZE + 1]; + weights.write[vertex_i * weight_8_count + 2] = weights_0[vertex_i * JOINT_GROUP_SIZE + 2]; + weights.write[vertex_i * weight_8_count + 3] = weights_0[vertex_i * JOINT_GROUP_SIZE + 3]; + weights.write[vertex_i * weight_8_count + 4] = weights_1[vertex_i * JOINT_GROUP_SIZE + 0]; + weights.write[vertex_i * weight_8_count + 5] = weights_1[vertex_i * JOINT_GROUP_SIZE + 1]; + weights.write[vertex_i * weight_8_count + 6] = weights_1[vertex_i * JOINT_GROUP_SIZE + 2]; + weights.write[vertex_i * weight_8_count + 7] = weights_1[vertex_i * JOINT_GROUP_SIZE + 3]; + } + { //gltf does not seem to normalize the weights for some reason.. + int wc = weights.size(); + float *w = weights.ptrw(); + + for (int k = 0; k < wc; k += weight_8_count) { + float total = 0.0; + total += w[k + 0]; + total += w[k + 1]; + total += w[k + 2]; + total += w[k + 3]; + total += w[k + 4]; + total += w[k + 5]; + total += w[k + 6]; + total += w[k + 7]; + if (total > 0.0) { + w[k + 0] /= total; + w[k + 1] /= total; + w[k + 2] /= total; + w[k + 3] /= total; + w[k + 4] /= total; + w[k + 5] /= total; + w[k + 6] /= total; + w[k + 7] /= total; + } + } + } + array[Mesh::ARRAY_WEIGHTS] = weights; + } + + if (p.has("indices")) { + Vector<int> indices = _decode_accessor_as_ints(state, p["indices"], false); + + if (primitive == Mesh::PRIMITIVE_TRIANGLES) { + //swap around indices, convert ccw to cw for front face + + const int is = indices.size(); + int *w = indices.ptrw(); + for (int k = 0; k < is; k += 3) { + SWAP(w[k + 1], w[k + 2]); + } + } + array[Mesh::ARRAY_INDEX] = indices; + + } else if (primitive == Mesh::PRIMITIVE_TRIANGLES) { + //generate indices because they need to be swapped for CW/CCW + const Vector<Vector3> &vertices = array[Mesh::ARRAY_VERTEX]; + ERR_FAIL_COND_V(vertices.size() == 0, ERR_PARSE_ERROR); + Vector<int> indices; + const int vs = vertices.size(); + indices.resize(vs); + { + int *w = indices.ptrw(); + for (int k = 0; k < vs; k += 3) { + w[k] = k; + w[k + 1] = k + 2; + w[k + 2] = k + 1; + } + } + array[Mesh::ARRAY_INDEX] = indices; + } + + bool generate_tangents = (primitive == Mesh::PRIMITIVE_TRIANGLES && !a.has("TANGENT") && a.has("TEXCOORD_0") && a.has("NORMAL")); + + if (generate_tangents) { + //must generate mikktspace tangents.. ergh.. + Ref<SurfaceTool> st; + st.instance(); + if (a.has("JOINTS_0") && a.has("JOINTS_1")) { + st->set_skin_weight_count(SurfaceTool::SKIN_8_WEIGHTS); + } + st->create_from_triangle_arrays(array); + st->generate_tangents(); + array = st->commit_to_arrays(); + } + + Array morphs; + //blend shapes + if (p.has("targets")) { + print_verbose("glTF: Mesh has targets"); + const Array &targets = p["targets"]; + + //ideally BLEND_SHAPE_MODE_RELATIVE since gltf2 stores in displacement + //but it could require a larger refactor? + import_mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_NORMALIZED); + + if (j == 0) { + const Array &target_names = extras.has("targetNames") ? (Array)extras["targetNames"] : Array(); + for (int k = 0; k < targets.size(); k++) { + const String name = k < target_names.size() ? (String)target_names[k] : String("morph_") + itos(k); + import_mesh->add_blend_shape(name); + } + } + + for (int k = 0; k < targets.size(); k++) { + const Dictionary &t = targets[k]; + + Array array_copy; + array_copy.resize(Mesh::ARRAY_MAX); + + for (int l = 0; l < Mesh::ARRAY_MAX; l++) { + array_copy[l] = array[l]; + } + + array_copy[Mesh::ARRAY_INDEX] = Variant(); + + if (t.has("POSITION")) { + Vector<Vector3> varr = _decode_accessor_as_vec3(state, t["POSITION"], true); + const Vector<Vector3> src_varr = array[Mesh::ARRAY_VERTEX]; + const int size = src_varr.size(); + ERR_FAIL_COND_V(size == 0, ERR_PARSE_ERROR); + { + const int max_idx = varr.size(); + varr.resize(size); + + Vector3 *w_varr = varr.ptrw(); + const Vector3 *r_varr = varr.ptr(); + const Vector3 *r_src_varr = src_varr.ptr(); + for (int l = 0; l < size; l++) { + if (l < max_idx) { + w_varr[l] = r_varr[l] + r_src_varr[l]; + } else { + w_varr[l] = r_src_varr[l]; + } + } + } + array_copy[Mesh::ARRAY_VERTEX] = varr; + } + if (t.has("NORMAL")) { + Vector<Vector3> narr = _decode_accessor_as_vec3(state, t["NORMAL"], true); + const Vector<Vector3> src_narr = array[Mesh::ARRAY_NORMAL]; + int size = src_narr.size(); + ERR_FAIL_COND_V(size == 0, ERR_PARSE_ERROR); + { + int max_idx = narr.size(); + narr.resize(size); + + Vector3 *w_narr = narr.ptrw(); + const Vector3 *r_narr = narr.ptr(); + const Vector3 *r_src_narr = src_narr.ptr(); + for (int l = 0; l < size; l++) { + if (l < max_idx) { + w_narr[l] = r_narr[l] + r_src_narr[l]; + } else { + w_narr[l] = r_src_narr[l]; + } + } + } + array_copy[Mesh::ARRAY_NORMAL] = narr; + } + if (t.has("TANGENT")) { + const Vector<Vector3> tangents_v3 = _decode_accessor_as_vec3(state, t["TANGENT"], true); + const Vector<float> src_tangents = array[Mesh::ARRAY_TANGENT]; + ERR_FAIL_COND_V(src_tangents.size() == 0, ERR_PARSE_ERROR); + + Vector<float> tangents_v4; + + { + int max_idx = tangents_v3.size(); + + int size4 = src_tangents.size(); + tangents_v4.resize(size4); + float *w4 = tangents_v4.ptrw(); + + const Vector3 *r3 = tangents_v3.ptr(); + const float *r4 = src_tangents.ptr(); + + for (int l = 0; l < size4 / 4; l++) { + if (l < max_idx) { + w4[l * 4 + 0] = r3[l].x + r4[l * 4 + 0]; + w4[l * 4 + 1] = r3[l].y + r4[l * 4 + 1]; + w4[l * 4 + 2] = r3[l].z + r4[l * 4 + 2]; + } else { + w4[l * 4 + 0] = r4[l * 4 + 0]; + w4[l * 4 + 1] = r4[l * 4 + 1]; + w4[l * 4 + 2] = r4[l * 4 + 2]; + } + w4[l * 4 + 3] = r4[l * 4 + 3]; //copy flip value + } + } + + array_copy[Mesh::ARRAY_TANGENT] = tangents_v4; + } + + if (generate_tangents) { + Ref<SurfaceTool> st; + st.instance(); + if (a.has("JOINTS_0") && a.has("JOINTS_1")) { + st->set_skin_weight_count(SurfaceTool::SKIN_8_WEIGHTS); + } + st->create_from_triangle_arrays(array_copy); + st->deindex(); + st->generate_tangents(); + array_copy = st->commit_to_arrays(); + } + + morphs.push_back(array_copy); + } + } + + //just add it + + Ref<BaseMaterial3D> mat; + if (p.has("material")) { + const int material = p["material"]; + ERR_FAIL_INDEX_V(material, state->materials.size(), ERR_FILE_CORRUPT); + Ref<BaseMaterial3D> mat3d = state->materials[material]; + if (has_vertex_color) { + mat3d->set_flag(BaseMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + } + mat = mat3d; + + } else if (has_vertex_color) { + Ref<StandardMaterial3D> mat3d; + mat3d.instance(); + mat3d->set_flag(BaseMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + mat = mat3d; + } + + import_mesh->add_surface(primitive, array, morphs, Dictionary(), mat); + } + + Vector<float> blend_weights; + blend_weights.resize(import_mesh->get_blend_shape_count()); + for (int32_t weight_i = 0; weight_i < blend_weights.size(); weight_i++) { + blend_weights.write[weight_i] = 0.0f; + } + + if (d.has("weights")) { + const Array &weights = d["weights"]; + for (int j = 0; j < weights.size(); j++) { + if (j >= blend_weights.size()) { + break; + } + blend_weights.write[j] = weights[j]; + } + mesh->set_blend_weights(blend_weights); + } + mesh->set_mesh(import_mesh); + + state->meshes.push_back(mesh); + } + + print_verbose("glTF: Total meshes: " + itos(state->meshes.size())); + + return OK; +} + +Error GLTFDocument::_serialize_images(Ref<GLTFState> state, const String &p_path) { + Array images; + for (int i = 0; i < state->images.size(); i++) { + Dictionary d; + + ERR_CONTINUE(state->images[i].is_null()); + + Ref<Image> image = state->images[i]->get_data(); + ERR_CONTINUE(image.is_null()); + + if (p_path.to_lower().ends_with("glb")) { + GLTFBufferViewIndex bvi; + + Ref<GLTFBufferView> bv; + bv.instance(); + + const GLTFBufferIndex bi = 0; + bv->buffer = bi; + bv->byte_offset = state->buffers[bi].size(); + ERR_FAIL_INDEX_V(bi, state->buffers.size(), ERR_PARAMETER_RANGE_ERROR); + + Vector<uint8_t> buffer; + Ref<ImageTexture> img_tex = image; + if (img_tex.is_valid()) { + image = img_tex->get_data(); + } + Error err = PNGDriverCommon::image_to_png(image, buffer); + ERR_FAIL_COND_V_MSG(err, err, "Can't convert image to PNG."); + + bv->byte_length = buffer.size(); + state->buffers.write[bi].resize(state->buffers[bi].size() + bv->byte_length); + copymem(&state->buffers.write[bi].write[bv->byte_offset], buffer.ptr(), buffer.size()); + ERR_FAIL_COND_V(bv->byte_offset + bv->byte_length > state->buffers[bi].size(), ERR_FILE_CORRUPT); + + state->buffer_views.push_back(bv); + bvi = state->buffer_views.size() - 1; + d["bufferView"] = bvi; + d["mimeType"] = "image/png"; + } else { + String name = state->images[i]->get_name(); + if (name.is_empty()) { + name = itos(i); + } + name = _gen_unique_name(state, name); + name = name.pad_zeros(3); + Ref<_Directory> dir; + dir.instance(); + String texture_dir = "textures"; + String new_texture_dir = p_path.get_base_dir() + "/" + texture_dir; + dir->open(p_path.get_base_dir()); + if (!dir->dir_exists(new_texture_dir)) { + dir->make_dir(new_texture_dir); + } + name = name + ".png"; + image->save_png(new_texture_dir.plus_file(name)); + d["uri"] = texture_dir.plus_file(name); + } + images.push_back(d); + } + + print_verbose("Total images: " + itos(state->images.size())); + + if (!images.size()) { + return OK; + } + state->json["images"] = images; + + return OK; +} + +Error GLTFDocument::_parse_images(Ref<GLTFState> state, const String &p_base_path) { + if (!state->json.has("images")) { + return OK; + } + + // Ref: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#images + + const Array &images = state->json["images"]; + for (int i = 0; i < images.size(); i++) { + const Dictionary &d = images[i]; + + // glTF 2.0 supports PNG and JPEG types, which can be specified as (from spec): + // "- a URI to an external file in one of the supported images formats, or + // - a URI with embedded base64-encoded data, or + // - a reference to a bufferView; in that case mimeType must be defined." + // Since mimeType is optional for external files and base64 data, we'll have to + // fall back on letting Godot parse the data to figure out if it's PNG or JPEG. + + // We'll assume that we use either URI or bufferView, so let's warn the user + // if their image somehow uses both. And fail if it has neither. + ERR_CONTINUE_MSG(!d.has("uri") && !d.has("bufferView"), "Invalid image definition in glTF file, it should specific an 'uri' or 'bufferView'."); + if (d.has("uri") && d.has("bufferView")) { + WARN_PRINT("Invalid image definition in glTF file using both 'uri' and 'bufferView'. 'bufferView' will take precedence."); + } + + String mimetype; + if (d.has("mimeType")) { // Should be "image/png" or "image/jpeg". + mimetype = d["mimeType"]; + } + + Vector<uint8_t> data; + const uint8_t *data_ptr = nullptr; + int data_size = 0; + + if (d.has("uri")) { + // Handles the first two bullet points from the spec (embedded data, or external file). + String uri = d["uri"]; + + if (uri.begins_with("data:")) { // Embedded data using base64. + // Validate data MIME types and throw a warning if it's one we don't know/support. + if (!uri.begins_with("data:application/octet-stream;base64") && + !uri.begins_with("data:application/gltf-buffer;base64") && + !uri.begins_with("data:image/png;base64") && + !uri.begins_with("data:image/jpeg;base64")) { + WARN_PRINT(vformat("glTF: Image index '%d' uses an unsupported URI data type: %s. Skipping it.", i, uri)); + state->images.push_back(Ref<Texture2D>()); // Placeholder to keep count. + continue; + } + data = _parse_base64_uri(uri); + data_ptr = data.ptr(); + data_size = data.size(); + // mimeType is optional, but if we have it defined in the URI, let's use it. + if (mimetype.is_empty()) { + if (uri.begins_with("data:image/png;base64")) { + mimetype = "image/png"; + } else if (uri.begins_with("data:image/jpeg;base64")) { + mimetype = "image/jpeg"; + } + } + } else { // Relative path to an external image file. + uri = p_base_path.plus_file(uri).replace("\\", "/"); // Fix for Windows. + // ResourceLoader will rely on the file extension to use the relevant loader. + // The spec says that if mimeType is defined, it should take precedence (e.g. + // there could be a `.png` image which is actually JPEG), but there's no easy + // API for that in Godot, so we'd have to load as a buffer (i.e. embedded in + // the material), so we do this only as fallback. + Ref<Texture2D> texture = ResourceLoader::load(uri); + if (texture.is_valid()) { + state->images.push_back(texture); + continue; + } else if (mimetype == "image/png" || mimetype == "image/jpeg") { + // Fallback to loading as byte array. + // This enables us to support the spec's requirement that we honor mimetype + // regardless of file URI. + data = FileAccess::get_file_as_array(uri); + if (data.size() == 0) { + WARN_PRINT(vformat("glTF: Image index '%d' couldn't be loaded as a buffer of MIME type '%s' from URI: %s. Skipping it.", i, mimetype, uri)); + state->images.push_back(Ref<Texture2D>()); // Placeholder to keep count. + continue; + } + data_ptr = data.ptr(); + data_size = data.size(); + } else { + WARN_PRINT(vformat("glTF: Image index '%d' couldn't be loaded from URI: %s. Skipping it.", i, uri)); + state->images.push_back(Ref<Texture2D>()); // Placeholder to keep count. + continue; + } + } + } else if (d.has("bufferView")) { + // Handles the third bullet point from the spec (bufferView). + ERR_FAIL_COND_V_MSG(mimetype.is_empty(), ERR_FILE_CORRUPT, + vformat("glTF: Image index '%d' specifies 'bufferView' but no 'mimeType', which is invalid.", i)); + + const GLTFBufferViewIndex bvi = d["bufferView"]; + + ERR_FAIL_INDEX_V(bvi, state->buffer_views.size(), ERR_PARAMETER_RANGE_ERROR); + + Ref<GLTFBufferView> bv = state->buffer_views[bvi]; + + const GLTFBufferIndex bi = bv->buffer; + ERR_FAIL_INDEX_V(bi, state->buffers.size(), ERR_PARAMETER_RANGE_ERROR); + + ERR_FAIL_COND_V(bv->byte_offset + bv->byte_length > state->buffers[bi].size(), ERR_FILE_CORRUPT); + + data_ptr = &state->buffers[bi][bv->byte_offset]; + data_size = bv->byte_length; + } + + Ref<Image> img; + + if (mimetype == "image/png") { // Load buffer as PNG. + ERR_FAIL_COND_V(Image::_png_mem_loader_func == nullptr, ERR_UNAVAILABLE); + img = Image::_png_mem_loader_func(data_ptr, data_size); + } else if (mimetype == "image/jpeg") { // Loader buffer as JPEG. + ERR_FAIL_COND_V(Image::_jpg_mem_loader_func == nullptr, ERR_UNAVAILABLE); + img = Image::_jpg_mem_loader_func(data_ptr, data_size); + } else { + // We can land here if we got an URI with base64-encoded data with application/* MIME type, + // and the optional mimeType property was not defined to tell us how to handle this data (or was invalid). + // So let's try PNG first, then JPEG. + ERR_FAIL_COND_V(Image::_png_mem_loader_func == nullptr, ERR_UNAVAILABLE); + img = Image::_png_mem_loader_func(data_ptr, data_size); + if (img.is_null()) { + ERR_FAIL_COND_V(Image::_jpg_mem_loader_func == nullptr, ERR_UNAVAILABLE); + img = Image::_jpg_mem_loader_func(data_ptr, data_size); + } + } + + ERR_FAIL_COND_V_MSG(img.is_null(), ERR_FILE_CORRUPT, + vformat("glTF: Couldn't load image index '%d' with its given mimetype: %s.", i, mimetype)); + + Ref<ImageTexture> t; + t.instance(); + t->create_from_image(img); + + state->images.push_back(t); + } + + print_verbose("glTF: Total images: " + itos(state->images.size())); + + return OK; +} + +Error GLTFDocument::_serialize_textures(Ref<GLTFState> state) { + if (!state->textures.size()) { + return OK; + } + + Array textures; + for (int32_t i = 0; i < state->textures.size(); i++) { + Dictionary d; + Ref<GLTFTexture> t = state->textures[i]; + ERR_CONTINUE(t->get_src_image() == -1); + d["source"] = t->get_src_image(); + textures.push_back(d); + } + state->json["textures"] = textures; + + return OK; +} + +Error GLTFDocument::_parse_textures(Ref<GLTFState> state) { + if (!state->json.has("textures")) + return OK; + + const Array &textures = state->json["textures"]; + for (GLTFTextureIndex i = 0; i < textures.size(); i++) { + const Dictionary &d = textures[i]; + + ERR_FAIL_COND_V(!d.has("source"), ERR_PARSE_ERROR); + + Ref<GLTFTexture> t; + t.instance(); + t->set_src_image(d["source"]); + state->textures.push_back(t); + } + + return OK; +} + +GLTFTextureIndex GLTFDocument::_set_texture(Ref<GLTFState> state, Ref<Texture2D> p_texture) { + ERR_FAIL_COND_V(p_texture.is_null(), -1); + Ref<GLTFTexture> gltf_texture; + gltf_texture.instance(); + ERR_FAIL_COND_V(p_texture->get_data().is_null(), -1); + GLTFImageIndex gltf_src_image_i = state->images.size(); + state->images.push_back(p_texture); + gltf_texture->set_src_image(gltf_src_image_i); + GLTFTextureIndex gltf_texture_i = state->textures.size(); + state->textures.push_back(gltf_texture); + return gltf_texture_i; +} + +Ref<Texture2D> GLTFDocument::_get_texture(Ref<GLTFState> state, const GLTFTextureIndex p_texture) { + ERR_FAIL_INDEX_V(p_texture, state->textures.size(), Ref<Texture2D>()); + const GLTFImageIndex image = state->textures[p_texture]->get_src_image(); + + ERR_FAIL_INDEX_V(image, state->images.size(), Ref<Texture2D>()); + + return state->images[image]; +} + +Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) { + Array materials; + for (int32_t i = 0; i < state->materials.size(); i++) { + Dictionary d; + + Ref<BaseMaterial3D> material = state->materials[i]; + if (material.is_null()) { + materials.push_back(d); + continue; + } + if (!material->get_name().is_empty()) { + d["name"] = _gen_unique_name(state, material->get_name()); + } + { + Dictionary mr; + { + Array arr; + const Color c = material->get_albedo().to_linear(); + arr.push_back(c.r); + arr.push_back(c.g); + arr.push_back(c.b); + arr.push_back(c.a); + mr["baseColorFactor"] = arr; + } + { + Dictionary bct; + Ref<Texture2D> albedo_texture = material->get_texture(BaseMaterial3D::TEXTURE_ALBEDO); + GLTFTextureIndex gltf_texture_index = -1; + + if (albedo_texture.is_valid() && albedo_texture->get_data().is_valid()) { + albedo_texture->set_name(material->get_name() + "_albedo"); + gltf_texture_index = _set_texture(state, albedo_texture); + } + if (gltf_texture_index != -1) { + bct["index"] = gltf_texture_index; + bct["extensions"] = _serialize_texture_transform_uv1(material); + mr["baseColorTexture"] = bct; + } + } + + mr["metallicFactor"] = material->get_metallic(); + mr["roughnessFactor"] = material->get_roughness(); + bool has_roughness = material->get_texture(BaseMaterial3D::TEXTURE_ROUGHNESS).is_valid() && material->get_texture(BaseMaterial3D::TEXTURE_ROUGHNESS)->get_data().is_valid(); + bool has_ao = material->get_feature(BaseMaterial3D::FEATURE_AMBIENT_OCCLUSION) && material->get_texture(BaseMaterial3D::TEXTURE_AMBIENT_OCCLUSION).is_valid(); + bool has_metalness = material->get_texture(BaseMaterial3D::TEXTURE_METALLIC).is_valid() && material->get_texture(BaseMaterial3D::TEXTURE_METALLIC)->get_data().is_valid(); + if (has_ao || has_roughness || has_metalness) { + Dictionary mrt; + Ref<Texture2D> roughness_texture = material->get_texture(BaseMaterial3D::TEXTURE_ROUGHNESS); + BaseMaterial3D::TextureChannel roughness_channel = material->get_roughness_texture_channel(); + Ref<Texture2D> metallic_texture = material->get_texture(BaseMaterial3D::TEXTURE_METALLIC); + BaseMaterial3D::TextureChannel metalness_channel = material->get_metallic_texture_channel(); + Ref<Texture2D> ao_texture = material->get_texture(BaseMaterial3D::TEXTURE_AMBIENT_OCCLUSION); + BaseMaterial3D::TextureChannel ao_channel = material->get_ao_texture_channel(); + Ref<ImageTexture> orm_texture; + orm_texture.instance(); + Ref<Image> orm_image; + orm_image.instance(); + int32_t height = 0; + int32_t width = 0; + Ref<Image> ao_image; + if (has_ao) { + height = ao_texture->get_height(); + width = ao_texture->get_width(); + ao_image = ao_texture->get_data(); + Ref<ImageTexture> img_tex = ao_image; + if (img_tex.is_valid()) { + ao_image = img_tex->get_data(); + } + if (ao_image->is_compressed()) { + ao_image->decompress(); + } + } + Ref<Image> roughness_image; + if (has_roughness) { + height = roughness_texture->get_height(); + width = roughness_texture->get_width(); + roughness_image = roughness_texture->get_data(); + Ref<ImageTexture> img_tex = roughness_image; + if (img_tex.is_valid()) { + roughness_image = img_tex->get_data(); + } + if (roughness_image->is_compressed()) { + roughness_image->decompress(); + } + } + Ref<Image> metallness_image; + if (has_metalness) { + height = metallic_texture->get_height(); + width = metallic_texture->get_width(); + metallness_image = metallic_texture->get_data(); + Ref<ImageTexture> img_tex = metallness_image; + if (img_tex.is_valid()) { + metallness_image = img_tex->get_data(); + } + if (metallness_image->is_compressed()) { + metallness_image->decompress(); + } + } + Ref<Texture2D> albedo_texture = material->get_texture(BaseMaterial3D::TEXTURE_ALBEDO); + if (albedo_texture.is_valid() && albedo_texture->get_data().is_valid()) { + height = albedo_texture->get_height(); + width = albedo_texture->get_width(); + } + orm_image->create(width, height, false, Image::FORMAT_RGBA8); + if (ao_image.is_valid() && ao_image->get_size() != Vector2(width, height)) { + ao_image->resize(width, height, Image::INTERPOLATE_LANCZOS); + } + if (roughness_image.is_valid() && roughness_image->get_size() != Vector2(width, height)) { + roughness_image->resize(width, height, Image::INTERPOLATE_LANCZOS); + } + if (metallness_image.is_valid() && metallness_image->get_size() != Vector2(width, height)) { + metallness_image->resize(width, height, Image::INTERPOLATE_LANCZOS); + } + for (int32_t h = 0; h < height; h++) { + for (int32_t w = 0; w < width; w++) { + Color c = Color(1.0f, 1.0f, 1.0f); + if (has_ao) { + if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_RED == ao_channel) { + c.r = ao_image->get_pixel(w, h).r; + } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_GREEN == ao_channel) { + c.r = ao_image->get_pixel(w, h).g; + } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_BLUE == ao_channel) { + c.r = ao_image->get_pixel(w, h).b; + } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_ALPHA == ao_channel) { + c.r = ao_image->get_pixel(w, h).a; + } + } + if (has_roughness) { + if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_RED == roughness_channel) { + c.g = roughness_image->get_pixel(w, h).r; + } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_GREEN == roughness_channel) { + c.g = roughness_image->get_pixel(w, h).g; + } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_BLUE == roughness_channel) { + c.g = roughness_image->get_pixel(w, h).b; + } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_ALPHA == roughness_channel) { + c.g = roughness_image->get_pixel(w, h).a; + } + } + if (has_metalness) { + if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_RED == metalness_channel) { + c.b = metallness_image->get_pixel(w, h).r; + } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_GREEN == metalness_channel) { + c.b = metallness_image->get_pixel(w, h).g; + } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_BLUE == metalness_channel) { + c.b = metallness_image->get_pixel(w, h).b; + } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_ALPHA == metalness_channel) { + c.b = metallness_image->get_pixel(w, h).a; + } + } + orm_image->set_pixel(w, h, c); + } + } + orm_image->generate_mipmaps(); + orm_texture->create_from_image(orm_image); + GLTFTextureIndex orm_texture_index = -1; + if (has_ao || has_roughness || has_metalness) { + orm_texture->set_name(material->get_name() + "_orm"); + orm_texture_index = _set_texture(state, orm_texture); + } + if (has_ao) { + Dictionary ot; + ot["index"] = orm_texture_index; + d["occlusionTexture"] = ot; + } + if (has_roughness || has_metalness) { + mrt["index"] = orm_texture_index; + mrt["extensions"] = _serialize_texture_transform_uv1(material); + mr["metallicRoughnessTexture"] = mrt; + } + } + d["pbrMetallicRoughness"] = mr; + } + + if (material->get_feature(BaseMaterial3D::FEATURE_NORMAL_MAPPING)) { + Dictionary nt; + Ref<ImageTexture> tex; + tex.instance(); + { + Ref<Texture2D> normal_texture = material->get_texture(BaseMaterial3D::TEXTURE_NORMAL); + // Code for uncompressing RG normal maps + Ref<Image> img = normal_texture->get_data(); + Ref<ImageTexture> img_tex = img; + if (img_tex.is_valid()) { + img = img_tex->get_data(); + } + img->decompress(); + img->convert(Image::FORMAT_RGBA8); + for (int32_t y = 0; y < img->get_height(); y++) { + for (int32_t x = 0; x < img->get_width(); x++) { + Color c = img->get_pixel(x, y); + Vector2 red_green = Vector2(c.r, c.g); + red_green = red_green * Vector2(2.0f, 2.0f) - Vector2(1.0f, 1.0f); + float blue = 1.0f - red_green.dot(red_green); + blue = MAX(0.0f, blue); + c.b = Math::sqrt(blue); + img->set_pixel(x, y, c); + } + } + tex->create_from_image(img); + } + Ref<Texture2D> normal_texture = material->get_texture(BaseMaterial3D::TEXTURE_NORMAL); + GLTFTextureIndex gltf_texture_index = -1; + if (tex.is_valid() && tex->get_data().is_valid()) { + tex->set_name(material->get_name() + "_normal"); + gltf_texture_index = _set_texture(state, tex); + } + nt["scale"] = material->get_normal_scale(); + if (gltf_texture_index != -1) { + nt["index"] = gltf_texture_index; + d["normalTexture"] = nt; + } + } + + if (material->get_feature(BaseMaterial3D::FEATURE_EMISSION)) { + const Color c = material->get_emission().to_srgb(); + Array arr; + arr.push_back(c.r); + arr.push_back(c.g); + arr.push_back(c.b); + d["emissiveFactor"] = arr; + } + if (material->get_feature(BaseMaterial3D::FEATURE_EMISSION)) { + Dictionary et; + Ref<Texture2D> emission_texture = material->get_texture(BaseMaterial3D::TEXTURE_EMISSION); + GLTFTextureIndex gltf_texture_index = -1; + if (emission_texture.is_valid() && emission_texture->get_data().is_valid()) { + emission_texture->set_name(material->get_name() + "_emission"); + gltf_texture_index = _set_texture(state, emission_texture); + } + + if (gltf_texture_index != -1) { + et["index"] = gltf_texture_index; + d["emissiveTexture"] = et; + } + } + const bool ds = material->get_cull_mode() == BaseMaterial3D::CULL_DISABLED; + if (ds) { + d["doubleSided"] = ds; + } + if (material->get_transparency() == BaseMaterial3D::TRANSPARENCY_ALPHA_SCISSOR) { + d["alphaMode"] = "MASK"; + d["alphaCutoff"] = material->get_alpha_scissor_threshold(); + } else if (material->get_transparency() != BaseMaterial3D::TRANSPARENCY_DISABLED) { + d["alphaMode"] = "BLEND"; + } + materials.push_back(d); + } + state->json["materials"] = materials; + print_verbose("Total materials: " + itos(state->materials.size())); + + return OK; +} + +Error GLTFDocument::_parse_materials(Ref<GLTFState> state) { + if (!state->json.has("materials")) + return OK; + + const Array &materials = state->json["materials"]; + for (GLTFMaterialIndex i = 0; i < materials.size(); i++) { + const Dictionary &d = materials[i]; + + Ref<StandardMaterial3D> material; + material.instance(); + if (d.has("name")) { + material->set_name(d["name"]); + } + material->set_flag(BaseMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + Dictionary pbr_spec_gloss_extensions; + if (d.has("extensions")) { + pbr_spec_gloss_extensions = d["extensions"]; + } + if (pbr_spec_gloss_extensions.has("KHR_materials_pbrSpecularGlossiness")) { + WARN_PRINT("Material uses a specular and glossiness workflow. Textures will be converted to roughness and metallic workflow, which may not be 100% accurate."); + Dictionary sgm = pbr_spec_gloss_extensions["KHR_materials_pbrSpecularGlossiness"]; + + Ref<GLTFSpecGloss> spec_gloss; + spec_gloss.instance(); + if (sgm.has("diffuseTexture")) { + const Dictionary &diffuse_texture_dict = sgm["diffuseTexture"]; + if (diffuse_texture_dict.has("index")) { + Ref<Texture2D> diffuse_texture = _get_texture(state, diffuse_texture_dict["index"]); + if (diffuse_texture.is_valid()) { + spec_gloss->diffuse_img = diffuse_texture->get_data(); + material->set_texture(BaseMaterial3D::TEXTURE_ALBEDO, diffuse_texture); + } + } + } + if (sgm.has("diffuseFactor")) { + const Array &arr = sgm["diffuseFactor"]; + ERR_FAIL_COND_V(arr.size() != 4, ERR_PARSE_ERROR); + const Color c = Color(arr[0], arr[1], arr[2], arr[3]).to_srgb(); + spec_gloss->diffuse_factor = c; + material->set_albedo(spec_gloss->diffuse_factor); + } + + if (sgm.has("specularFactor")) { + const Array &arr = sgm["specularFactor"]; + ERR_FAIL_COND_V(arr.size() != 3, ERR_PARSE_ERROR); + spec_gloss->specular_factor = Color(arr[0], arr[1], arr[2]); + } + + if (sgm.has("glossinessFactor")) { + spec_gloss->gloss_factor = sgm["glossinessFactor"]; + material->set_roughness(1.0f - CLAMP(spec_gloss->gloss_factor, 0.0f, 1.0f)); + } + if (sgm.has("specularGlossinessTexture")) { + const Dictionary &spec_gloss_texture = sgm["specularGlossinessTexture"]; + if (spec_gloss_texture.has("index")) { + const Ref<Texture2D> orig_texture = _get_texture(state, spec_gloss_texture["index"]); + if (orig_texture.is_valid()) { + spec_gloss->spec_gloss_img = orig_texture->get_data(); + } + } + } + spec_gloss_to_rough_metal(spec_gloss, material); + + } else if (d.has("pbrMetallicRoughness")) { + const Dictionary &mr = d["pbrMetallicRoughness"]; + if (mr.has("baseColorFactor")) { + const Array &arr = mr["baseColorFactor"]; + ERR_FAIL_COND_V(arr.size() != 4, ERR_PARSE_ERROR); + const Color c = Color(arr[0], arr[1], arr[2], arr[3]).to_srgb(); + material->set_albedo(c); + } + + if (mr.has("baseColorTexture")) { + const Dictionary &bct = mr["baseColorTexture"]; + if (bct.has("index")) { + material->set_texture(BaseMaterial3D::TEXTURE_ALBEDO, _get_texture(state, bct["index"])); + } + if (!mr.has("baseColorFactor")) { + material->set_albedo(Color(1, 1, 1)); + } + _set_texture_transform_uv1(bct, material); + } + + if (mr.has("metallicFactor")) { + material->set_metallic(mr["metallicFactor"]); + } else { + material->set_metallic(1.0); + } + + if (mr.has("roughnessFactor")) { + material->set_roughness(mr["roughnessFactor"]); + } else { + material->set_roughness(1.0); + } + + if (mr.has("metallicRoughnessTexture")) { + const Dictionary &bct = mr["metallicRoughnessTexture"]; + if (bct.has("index")) { + const Ref<Texture2D> t = _get_texture(state, bct["index"]); + material->set_texture(BaseMaterial3D::TEXTURE_METALLIC, t); + material->set_metallic_texture_channel(BaseMaterial3D::TEXTURE_CHANNEL_BLUE); + material->set_texture(BaseMaterial3D::TEXTURE_ROUGHNESS, t); + material->set_roughness_texture_channel(BaseMaterial3D::TEXTURE_CHANNEL_GREEN); + if (!mr.has("metallicFactor")) { + material->set_metallic(1); + } + if (!mr.has("roughnessFactor")) { + material->set_roughness(1); + } + } + } + } + + if (d.has("normalTexture")) { + const Dictionary &bct = d["normalTexture"]; + if (bct.has("index")) { + material->set_texture(BaseMaterial3D::TEXTURE_NORMAL, _get_texture(state, bct["index"])); + material->set_feature(BaseMaterial3D::FEATURE_NORMAL_MAPPING, true); + } + if (bct.has("scale")) { + material->set_normal_scale(bct["scale"]); + } + } + if (d.has("occlusionTexture")) { + const Dictionary &bct = d["occlusionTexture"]; + if (bct.has("index")) { + material->set_texture(BaseMaterial3D::TEXTURE_AMBIENT_OCCLUSION, _get_texture(state, bct["index"])); + material->set_ao_texture_channel(BaseMaterial3D::TEXTURE_CHANNEL_RED); + material->set_feature(BaseMaterial3D::FEATURE_AMBIENT_OCCLUSION, true); + } + } + + if (d.has("emissiveFactor")) { + const Array &arr = d["emissiveFactor"]; + ERR_FAIL_COND_V(arr.size() != 3, ERR_PARSE_ERROR); + const Color c = Color(arr[0], arr[1], arr[2]).to_srgb(); + material->set_feature(BaseMaterial3D::FEATURE_EMISSION, true); + + material->set_emission(c); + } + + if (d.has("emissiveTexture")) { + const Dictionary &bct = d["emissiveTexture"]; + if (bct.has("index")) { + material->set_texture(BaseMaterial3D::TEXTURE_EMISSION, _get_texture(state, bct["index"])); + material->set_feature(BaseMaterial3D::FEATURE_EMISSION, true); + material->set_emission(Color(0, 0, 0)); + } + } + + if (d.has("doubleSided")) { + const bool ds = d["doubleSided"]; + if (ds) { + material->set_cull_mode(BaseMaterial3D::CULL_DISABLED); + } + } + + if (d.has("alphaMode")) { + const String &am = d["alphaMode"]; + if (am == "BLEND") { + material->set_transparency(BaseMaterial3D::TRANSPARENCY_ALPHA_DEPTH_PRE_PASS); + } else if (am == "MASK") { + material->set_transparency(BaseMaterial3D::TRANSPARENCY_ALPHA_SCISSOR); + if (d.has("alphaCutoff")) { + material->set_alpha_scissor_threshold(d["alphaCutoff"]); + } else { + material->set_alpha_scissor_threshold(0.5f); + } + } + } + state->materials.push_back(material); + } + + print_verbose("Total materials: " + itos(state->materials.size())); + + return OK; +} + +void GLTFDocument::_set_texture_transform_uv1(const Dictionary &d, Ref<BaseMaterial3D> material) { + if (d.has("extensions")) { + const Dictionary &extensions = d["extensions"]; + if (extensions.has("KHR_texture_transform")) { + const Dictionary &texture_transform = extensions["KHR_texture_transform"]; + const Array &offset_arr = texture_transform["offset"]; + if (offset_arr.size() == 2) { + const Vector3 offset_vector3 = Vector3(offset_arr[0], offset_arr[1], 0.0f); + material->set_uv1_offset(offset_vector3); + } + + const Array &scale_arr = texture_transform["scale"]; + if (scale_arr.size() == 2) { + const Vector3 scale_vector3 = Vector3(scale_arr[0], scale_arr[1], 1.0f); + material->set_uv1_scale(scale_vector3); + } + } + } +} + +void GLTFDocument::spec_gloss_to_rough_metal(Ref<GLTFSpecGloss> r_spec_gloss, Ref<BaseMaterial3D> p_material) { + if (r_spec_gloss->spec_gloss_img.is_null()) { + return; + } + if (r_spec_gloss->diffuse_img.is_null()) { + return; + } + Ref<Image> rm_img; + rm_img.instance(); + bool has_roughness = false; + bool has_metal = false; + p_material->set_roughness(1.0f); + p_material->set_metallic(1.0f); + rm_img->create(r_spec_gloss->spec_gloss_img->get_width(), r_spec_gloss->spec_gloss_img->get_height(), false, Image::FORMAT_RGBA8); + r_spec_gloss->spec_gloss_img->decompress(); + if (r_spec_gloss->diffuse_img.is_valid()) { + r_spec_gloss->diffuse_img->decompress(); + r_spec_gloss->diffuse_img->resize(r_spec_gloss->spec_gloss_img->get_width(), r_spec_gloss->spec_gloss_img->get_height(), Image::INTERPOLATE_LANCZOS); + r_spec_gloss->spec_gloss_img->resize(r_spec_gloss->diffuse_img->get_width(), r_spec_gloss->diffuse_img->get_height(), Image::INTERPOLATE_LANCZOS); + } + for (int32_t y = 0; y < r_spec_gloss->spec_gloss_img->get_height(); y++) { + for (int32_t x = 0; x < r_spec_gloss->spec_gloss_img->get_width(); x++) { + const Color specular_pixel = r_spec_gloss->spec_gloss_img->get_pixel(x, y).to_linear(); + Color specular = Color(specular_pixel.r, specular_pixel.g, specular_pixel.b); + specular *= r_spec_gloss->specular_factor; + Color diffuse = Color(1.0f, 1.0f, 1.0f); + diffuse *= r_spec_gloss->diffuse_img->get_pixel(x, y).to_linear(); + float metallic = 0.0f; + Color base_color; + spec_gloss_to_metal_base_color(specular, diffuse, base_color, metallic); + Color mr = Color(1.0f, 1.0f, 1.0f); + mr.g = specular_pixel.a; + mr.b = metallic; + if (!Math::is_equal_approx(mr.g, 1.0f)) { + has_roughness = true; + } + if (!Math::is_equal_approx(mr.b, 0.0f)) { + has_metal = true; + } + mr.g *= r_spec_gloss->gloss_factor; + mr.g = 1.0f - mr.g; + rm_img->set_pixel(x, y, mr); + if (r_spec_gloss->diffuse_img.is_valid()) { + r_spec_gloss->diffuse_img->set_pixel(x, y, base_color.to_srgb()); + } + } + } + rm_img->generate_mipmaps(); + r_spec_gloss->diffuse_img->generate_mipmaps(); + Ref<ImageTexture> diffuse_image_texture; + diffuse_image_texture.instance(); + diffuse_image_texture->create_from_image(r_spec_gloss->diffuse_img); + p_material->set_texture(BaseMaterial3D::TEXTURE_ALBEDO, diffuse_image_texture); + Ref<ImageTexture> rm_image_texture; + rm_image_texture.instance(); + rm_image_texture->create_from_image(rm_img); + if (has_roughness) { + p_material->set_texture(BaseMaterial3D::TEXTURE_ROUGHNESS, rm_image_texture); + p_material->set_roughness_texture_channel(BaseMaterial3D::TEXTURE_CHANNEL_GREEN); + } + + if (has_metal) { + p_material->set_texture(BaseMaterial3D::TEXTURE_METALLIC, rm_image_texture); + p_material->set_metallic_texture_channel(BaseMaterial3D::TEXTURE_CHANNEL_BLUE); + } +} + +void GLTFDocument::spec_gloss_to_metal_base_color(const Color &p_specular_factor, const Color &p_diffuse, Color &r_base_color, float &r_metallic) { + const Color DIELECTRIC_SPECULAR = Color(0.04f, 0.04f, 0.04f); + Color specular = Color(p_specular_factor.r, p_specular_factor.g, p_specular_factor.b); + const float one_minus_specular_strength = 1.0f - get_max_component(specular); + const float dielectric_specular_red = DIELECTRIC_SPECULAR.r; + float brightness_diffuse = get_perceived_brightness(p_diffuse); + const float brightness_specular = get_perceived_brightness(specular); + r_metallic = solve_metallic(dielectric_specular_red, brightness_diffuse, brightness_specular, one_minus_specular_strength); + const float one_minus_metallic = 1.0f - r_metallic; + const Color base_color_from_diffuse = p_diffuse * (one_minus_specular_strength / (1.0f - dielectric_specular_red) / MAX(one_minus_metallic, CMP_EPSILON)); + const Color base_color_from_specular = (specular - (DIELECTRIC_SPECULAR * (one_minus_metallic))) * (1.0f / MAX(r_metallic, CMP_EPSILON)); + r_base_color.r = Math::lerp(base_color_from_diffuse.r, base_color_from_specular.r, r_metallic * r_metallic); + r_base_color.g = Math::lerp(base_color_from_diffuse.g, base_color_from_specular.g, r_metallic * r_metallic); + r_base_color.b = Math::lerp(base_color_from_diffuse.b, base_color_from_specular.b, r_metallic * r_metallic); + r_base_color.a = p_diffuse.a; + r_base_color.r = CLAMP(r_base_color.r, 0.0f, 1.0f); + r_base_color.g = CLAMP(r_base_color.g, 0.0f, 1.0f); + r_base_color.b = CLAMP(r_base_color.b, 0.0f, 1.0f); + r_base_color.a = CLAMP(r_base_color.a, 0.0f, 1.0f); +} + +GLTFNodeIndex GLTFDocument::_find_highest_node(Ref<GLTFState> state, const Vector<GLTFNodeIndex> &subset) { + int highest = -1; + GLTFNodeIndex best_node = -1; + + for (int i = 0; i < subset.size(); ++i) { + const GLTFNodeIndex node_i = subset[i]; + const Ref<GLTFNode> node = state->nodes[node_i]; + + if (highest == -1 || node->height < highest) { + highest = node->height; + best_node = node_i; + } + } + + return best_node; +} + +bool GLTFDocument::_capture_nodes_in_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin, const GLTFNodeIndex node_index) { + bool found_joint = false; + + for (int i = 0; i < state->nodes[node_index]->children.size(); ++i) { + found_joint |= _capture_nodes_in_skin(state, skin, state->nodes[node_index]->children[i]); + } + + if (found_joint) { + // Mark it if we happen to find another skins joint... + if (state->nodes[node_index]->joint && skin->joints.find(node_index) < 0) { + skin->joints.push_back(node_index); + } else if (skin->non_joints.find(node_index) < 0) { + skin->non_joints.push_back(node_index); + } + } + + if (skin->joints.find(node_index) > 0) { + return true; + } + + return false; +} + +void GLTFDocument::_capture_nodes_for_multirooted_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin) { + DisjointSet<GLTFNodeIndex> disjoint_set; + + for (int i = 0; i < skin->joints.size(); ++i) { + const GLTFNodeIndex node_index = skin->joints[i]; + const GLTFNodeIndex parent = state->nodes[node_index]->parent; + disjoint_set.insert(node_index); + + if (skin->joints.find(parent) >= 0) { + disjoint_set.create_union(parent, node_index); + } + } + + Vector<GLTFNodeIndex> roots; + disjoint_set.get_representatives(roots); + + if (roots.size() <= 1) { + return; + } + + int maxHeight = -1; + + // Determine the max height rooted tree + for (int i = 0; i < roots.size(); ++i) { + const GLTFNodeIndex root = roots[i]; + + if (maxHeight == -1 || state->nodes[root]->height < maxHeight) { + maxHeight = state->nodes[root]->height; + } + } + + // Go up the tree till all of the multiple roots of the skin are at the same hierarchy level. + // This sucks, but 99% of all game engines (not just Godot) would have this same issue. + for (int i = 0; i < roots.size(); ++i) { + GLTFNodeIndex current_node = roots[i]; + while (state->nodes[current_node]->height > maxHeight) { + GLTFNodeIndex parent = state->nodes[current_node]->parent; + + if (state->nodes[parent]->joint && skin->joints.find(parent) < 0) { + skin->joints.push_back(parent); + } else if (skin->non_joints.find(parent) < 0) { + skin->non_joints.push_back(parent); + } + + current_node = parent; + } + + // replace the roots + roots.write[i] = current_node; + } + + // Climb up the tree until they all have the same parent + bool all_same; + + do { + all_same = true; + const GLTFNodeIndex first_parent = state->nodes[roots[0]]->parent; + + for (int i = 1; i < roots.size(); ++i) { + all_same &= (first_parent == state->nodes[roots[i]]->parent); + } + + if (!all_same) { + for (int i = 0; i < roots.size(); ++i) { + const GLTFNodeIndex current_node = roots[i]; + const GLTFNodeIndex parent = state->nodes[current_node]->parent; + + if (state->nodes[parent]->joint && skin->joints.find(parent) < 0) { + skin->joints.push_back(parent); + } else if (skin->non_joints.find(parent) < 0) { + skin->non_joints.push_back(parent); + } + + roots.write[i] = parent; + } + } + + } while (!all_same); +} + +Error GLTFDocument::_expand_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin) { + _capture_nodes_for_multirooted_skin(state, skin); + + // Grab all nodes that lay in between skin joints/nodes + DisjointSet<GLTFNodeIndex> disjoint_set; + + Vector<GLTFNodeIndex> all_skin_nodes; + all_skin_nodes.append_array(skin->joints); + all_skin_nodes.append_array(skin->non_joints); + + for (int i = 0; i < all_skin_nodes.size(); ++i) { + const GLTFNodeIndex node_index = all_skin_nodes[i]; + const GLTFNodeIndex parent = state->nodes[node_index]->parent; + disjoint_set.insert(node_index); + + if (all_skin_nodes.find(parent) >= 0) { + disjoint_set.create_union(parent, node_index); + } + } + + Vector<GLTFNodeIndex> out_owners; + disjoint_set.get_representatives(out_owners); + + Vector<GLTFNodeIndex> out_roots; + + for (int i = 0; i < out_owners.size(); ++i) { + Vector<GLTFNodeIndex> set; + disjoint_set.get_members(set, out_owners[i]); + + const GLTFNodeIndex root = _find_highest_node(state, set); + ERR_FAIL_COND_V(root < 0, FAILED); + out_roots.push_back(root); + } + + out_roots.sort(); + + for (int i = 0; i < out_roots.size(); ++i) { + _capture_nodes_in_skin(state, skin, out_roots[i]); + } + + skin->roots = out_roots; + + return OK; +} + +Error GLTFDocument::_verify_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin) { + // This may seem duplicated from expand_skins, but this is really a sanity check! (so it kinda is) + // In case additional interpolating logic is added to the skins, this will help ensure that you + // do not cause it to self implode into a fiery blaze + + // We are going to re-calculate the root nodes and compare them to the ones saved in the skin, + // then ensure the multiple trees (if they exist) are on the same sublevel + + // Grab all nodes that lay in between skin joints/nodes + DisjointSet<GLTFNodeIndex> disjoint_set; + + Vector<GLTFNodeIndex> all_skin_nodes; + all_skin_nodes.append_array(skin->joints); + all_skin_nodes.append_array(skin->non_joints); + + for (int i = 0; i < all_skin_nodes.size(); ++i) { + const GLTFNodeIndex node_index = all_skin_nodes[i]; + const GLTFNodeIndex parent = state->nodes[node_index]->parent; + disjoint_set.insert(node_index); + + if (all_skin_nodes.find(parent) >= 0) { + disjoint_set.create_union(parent, node_index); + } + } + + Vector<GLTFNodeIndex> out_owners; + disjoint_set.get_representatives(out_owners); + + Vector<GLTFNodeIndex> out_roots; + + for (int i = 0; i < out_owners.size(); ++i) { + Vector<GLTFNodeIndex> set; + disjoint_set.get_members(set, out_owners[i]); + + const GLTFNodeIndex root = _find_highest_node(state, set); + ERR_FAIL_COND_V(root < 0, FAILED); + out_roots.push_back(root); + } + + out_roots.sort(); + + ERR_FAIL_COND_V(out_roots.size() == 0, FAILED); + + // Make sure the roots are the exact same (they better be) + ERR_FAIL_COND_V(out_roots.size() != skin->roots.size(), FAILED); + for (int i = 0; i < out_roots.size(); ++i) { + ERR_FAIL_COND_V(out_roots[i] != skin->roots[i], FAILED); + } + + // Single rooted skin? Perfectly ok! + if (out_roots.size() == 1) { + return OK; + } + + // Make sure all parents of a multi-rooted skin are the SAME + const GLTFNodeIndex parent = state->nodes[out_roots[0]]->parent; + for (int i = 1; i < out_roots.size(); ++i) { + if (state->nodes[out_roots[i]]->parent != parent) { + return FAILED; + } + } + + return OK; +} + +Error GLTFDocument::_parse_skins(Ref<GLTFState> state) { + if (!state->json.has("skins")) + return OK; + + const Array &skins = state->json["skins"]; + + // Create the base skins, and mark nodes that are joints + for (int i = 0; i < skins.size(); i++) { + const Dictionary &d = skins[i]; + + Ref<GLTFSkin> skin; + skin.instance(); + + ERR_FAIL_COND_V(!d.has("joints"), ERR_PARSE_ERROR); + + const Array &joints = d["joints"]; + + if (d.has("inverseBindMatrices")) { + skin->inverse_binds = _decode_accessor_as_xform(state, d["inverseBindMatrices"], false); + ERR_FAIL_COND_V(skin->inverse_binds.size() != joints.size(), ERR_PARSE_ERROR); + } + + for (int j = 0; j < joints.size(); j++) { + const GLTFNodeIndex node = joints[j]; + ERR_FAIL_INDEX_V(node, state->nodes.size(), ERR_PARSE_ERROR); + + skin->joints.push_back(node); + skin->joints_original.push_back(node); + + state->nodes.write[node]->joint = true; + } + + if (d.has("name")) { + skin->set_name(d["name"]); + } + + if (d.has("skeleton")) { + skin->skin_root = d["skeleton"]; + } + + state->skins.push_back(skin); + } + + for (GLTFSkinIndex i = 0; i < state->skins.size(); ++i) { + Ref<GLTFSkin> skin = state->skins.write[i]; + + // Expand the skin to capture all the extra non-joints that lie in between the actual joints, + // and expand the hierarchy to ensure multi-rooted trees lie on the same height level + ERR_FAIL_COND_V(_expand_skin(state, skin), ERR_PARSE_ERROR); + ERR_FAIL_COND_V(_verify_skin(state, skin), ERR_PARSE_ERROR); + } + + print_verbose("glTF: Total skins: " + itos(state->skins.size())); + + return OK; +} + +Error GLTFDocument::_determine_skeletons(Ref<GLTFState> state) { + // Using a disjoint set, we are going to potentially combine all skins that are actually branches + // of a main skeleton, or treat skins defining the same set of nodes as ONE skeleton. + // This is another unclear issue caused by the current glTF specification. + + DisjointSet<GLTFNodeIndex> skeleton_sets; + + for (GLTFSkinIndex skin_i = 0; skin_i < state->skins.size(); ++skin_i) { + const Ref<GLTFSkin> skin = state->skins[skin_i]; + + Vector<GLTFNodeIndex> all_skin_nodes; + all_skin_nodes.append_array(skin->joints); + all_skin_nodes.append_array(skin->non_joints); + + for (int i = 0; i < all_skin_nodes.size(); ++i) { + const GLTFNodeIndex node_index = all_skin_nodes[i]; + const GLTFNodeIndex parent = state->nodes[node_index]->parent; + skeleton_sets.insert(node_index); + + if (all_skin_nodes.find(parent) >= 0) { + skeleton_sets.create_union(parent, node_index); + } + } + + // We are going to connect the separate skin subtrees in each skin together + // so that the final roots are entire sets of valid skin trees + for (int i = 1; i < skin->roots.size(); ++i) { + skeleton_sets.create_union(skin->roots[0], skin->roots[i]); + } + } + + { // attempt to joint all touching subsets (siblings/parent are part of another skin) + Vector<GLTFNodeIndex> groups_representatives; + skeleton_sets.get_representatives(groups_representatives); + + Vector<GLTFNodeIndex> highest_group_members; + Vector<Vector<GLTFNodeIndex>> groups; + for (int i = 0; i < groups_representatives.size(); ++i) { + Vector<GLTFNodeIndex> group; + skeleton_sets.get_members(group, groups_representatives[i]); + highest_group_members.push_back(_find_highest_node(state, group)); + groups.push_back(group); + } + + for (int i = 0; i < highest_group_members.size(); ++i) { + const GLTFNodeIndex node_i = highest_group_members[i]; + + // Attach any siblings together (this needs to be done n^2/2 times) + for (int j = i + 1; j < highest_group_members.size(); ++j) { + const GLTFNodeIndex node_j = highest_group_members[j]; + + // Even if they are siblings under the root! :) + if (state->nodes[node_i]->parent == state->nodes[node_j]->parent) { + skeleton_sets.create_union(node_i, node_j); + } + } + + // Attach any parenting going on together (we need to do this n^2 times) + const GLTFNodeIndex node_i_parent = state->nodes[node_i]->parent; + if (node_i_parent >= 0) { + for (int j = 0; j < groups.size() && i != j; ++j) { + const Vector<GLTFNodeIndex> &group = groups[j]; + + if (group.find(node_i_parent) >= 0) { + const GLTFNodeIndex node_j = highest_group_members[j]; + skeleton_sets.create_union(node_i, node_j); + } + } + } + } + } + + // At this point, the skeleton groups should be finalized + Vector<GLTFNodeIndex> skeleton_owners; + skeleton_sets.get_representatives(skeleton_owners); + + // Mark all the skins actual skeletons, after we have merged them + for (GLTFSkeletonIndex skel_i = 0; skel_i < skeleton_owners.size(); ++skel_i) { + const GLTFNodeIndex skeleton_owner = skeleton_owners[skel_i]; + Ref<GLTFSkeleton> skeleton; + skeleton.instance(); + + Vector<GLTFNodeIndex> skeleton_nodes; + skeleton_sets.get_members(skeleton_nodes, skeleton_owner); + + for (GLTFSkinIndex skin_i = 0; skin_i < state->skins.size(); ++skin_i) { + Ref<GLTFSkin> skin = state->skins.write[skin_i]; + + // If any of the the skeletons nodes exist in a skin, that skin now maps to the skeleton + for (int i = 0; i < skeleton_nodes.size(); ++i) { + GLTFNodeIndex skel_node_i = skeleton_nodes[i]; + if (skin->joints.find(skel_node_i) >= 0 || skin->non_joints.find(skel_node_i) >= 0) { + skin->skeleton = skel_i; + continue; + } + } + } + + Vector<GLTFNodeIndex> non_joints; + for (int i = 0; i < skeleton_nodes.size(); ++i) { + const GLTFNodeIndex node_i = skeleton_nodes[i]; + + if (state->nodes[node_i]->joint) { + skeleton->joints.push_back(node_i); + } else { + non_joints.push_back(node_i); + } + } + + state->skeletons.push_back(skeleton); + + _reparent_non_joint_skeleton_subtrees(state, state->skeletons.write[skel_i], non_joints); + } + + for (GLTFSkeletonIndex skel_i = 0; skel_i < state->skeletons.size(); ++skel_i) { + Ref<GLTFSkeleton> skeleton = state->skeletons.write[skel_i]; + + for (int i = 0; i < skeleton->joints.size(); ++i) { + const GLTFNodeIndex node_i = skeleton->joints[i]; + Ref<GLTFNode> node = state->nodes[node_i]; + + ERR_FAIL_COND_V(!node->joint, ERR_PARSE_ERROR); + ERR_FAIL_COND_V(node->skeleton >= 0, ERR_PARSE_ERROR); + node->skeleton = skel_i; + } + + ERR_FAIL_COND_V(_determine_skeleton_roots(state, skel_i), ERR_PARSE_ERROR); + } + + return OK; +} + +Error GLTFDocument::_reparent_non_joint_skeleton_subtrees(Ref<GLTFState> state, Ref<GLTFSkeleton> skeleton, const Vector<GLTFNodeIndex> &non_joints) { + DisjointSet<GLTFNodeIndex> subtree_set; + + // Populate the disjoint set with ONLY non joints that are in the skeleton hierarchy (non_joints vector) + // This way we can find any joints that lie in between joints, as the current glTF specification + // mentions nothing about non-joints being in between joints of the same skin. Hopefully one day we + // can remove this code. + + // skinD depicted here explains this issue: + // https://github.com/KhronosGroup/glTF-Asset-Generator/blob/master/Output/Positive/Animation_Skin + + for (int i = 0; i < non_joints.size(); ++i) { + const GLTFNodeIndex node_i = non_joints[i]; + + subtree_set.insert(node_i); + + const GLTFNodeIndex parent_i = state->nodes[node_i]->parent; + if (parent_i >= 0 && non_joints.find(parent_i) >= 0 && !state->nodes[parent_i]->joint) { + subtree_set.create_union(parent_i, node_i); + } + } + + // Find all the non joint subtrees and re-parent them to a new "fake" joint + + Vector<GLTFNodeIndex> non_joint_subtree_roots; + subtree_set.get_representatives(non_joint_subtree_roots); + + for (int root_i = 0; root_i < non_joint_subtree_roots.size(); ++root_i) { + const GLTFNodeIndex subtree_root = non_joint_subtree_roots[root_i]; + + Vector<GLTFNodeIndex> subtree_nodes; + subtree_set.get_members(subtree_nodes, subtree_root); + + for (int subtree_i = 0; subtree_i < subtree_nodes.size(); ++subtree_i) { + ERR_FAIL_COND_V(_reparent_to_fake_joint(state, skeleton, subtree_nodes[subtree_i]), FAILED); + + // We modified the tree, recompute all the heights + _compute_node_heights(state); + } + } + + return OK; +} + +Error GLTFDocument::_reparent_to_fake_joint(Ref<GLTFState> state, Ref<GLTFSkeleton> skeleton, const GLTFNodeIndex node_index) { + Ref<GLTFNode> node = state->nodes[node_index]; + + // Can we just "steal" this joint if it is just a spatial node? + if (node->skin < 0 && node->mesh < 0 && node->camera < 0) { + node->joint = true; + // Add the joint to the skeletons joints + skeleton->joints.push_back(node_index); + return OK; + } + + GLTFNode *fake_joint = memnew(GLTFNode); + const GLTFNodeIndex fake_joint_index = state->nodes.size(); + state->nodes.push_back(fake_joint); + + // We better not be a joint, or we messed up in our logic + if (node->joint) + return FAILED; + + fake_joint->translation = node->translation; + fake_joint->rotation = node->rotation; + fake_joint->scale = node->scale; + fake_joint->xform = node->xform; + fake_joint->joint = true; + + // We can use the exact same name here, because the joint will be inside a skeleton and not the scene + fake_joint->set_name(node->get_name()); + + // Clear the nodes transforms, since it will be parented to the fake joint + node->translation = Vector3(0, 0, 0); + node->rotation = Quat(); + node->scale = Vector3(1, 1, 1); + node->xform = Transform(); + + // Transfer the node children to the fake joint + for (int child_i = 0; child_i < node->children.size(); ++child_i) { + Ref<GLTFNode> child = state->nodes[node->children[child_i]]; + child->parent = fake_joint_index; + } + + fake_joint->children = node->children; + node->children.clear(); + + // add the fake joint to the parent and remove the original joint + if (node->parent >= 0) { + Ref<GLTFNode> parent = state->nodes[node->parent]; + parent->children.erase(node_index); + parent->children.push_back(fake_joint_index); + fake_joint->parent = node->parent; + } + + // Add the node to the fake joint + fake_joint->children.push_back(node_index); + node->parent = fake_joint_index; + node->fake_joint_parent = fake_joint_index; + + // Add the fake joint to the skeletons joints + skeleton->joints.push_back(fake_joint_index); + + // Replace skin_skeletons with fake joints if we must. + for (GLTFSkinIndex skin_i = 0; skin_i < state->skins.size(); ++skin_i) { + Ref<GLTFSkin> skin = state->skins.write[skin_i]; + if (skin->skin_root == node_index) { + skin->skin_root = fake_joint_index; + } + } + + return OK; +} + +Error GLTFDocument::_determine_skeleton_roots(Ref<GLTFState> state, const GLTFSkeletonIndex skel_i) { + DisjointSet<GLTFNodeIndex> disjoint_set; + + for (GLTFNodeIndex i = 0; i < state->nodes.size(); ++i) { + const Ref<GLTFNode> node = state->nodes[i]; + + if (node->skeleton != skel_i) { + continue; + } + + disjoint_set.insert(i); + + if (node->parent >= 0 && state->nodes[node->parent]->skeleton == skel_i) { + disjoint_set.create_union(node->parent, i); + } + } + + Ref<GLTFSkeleton> skeleton = state->skeletons.write[skel_i]; + + Vector<GLTFNodeIndex> owners; + disjoint_set.get_representatives(owners); + + Vector<GLTFNodeIndex> roots; + + for (int i = 0; i < owners.size(); ++i) { + Vector<GLTFNodeIndex> set; + disjoint_set.get_members(set, owners[i]); + const GLTFNodeIndex root = _find_highest_node(state, set); + ERR_FAIL_COND_V(root < 0, FAILED); + roots.push_back(root); + } + + roots.sort(); + + skeleton->roots = roots; + + if (roots.size() == 0) { + return FAILED; + } else if (roots.size() == 1) { + return OK; + } + + // Check that the subtrees have the same parent root + const GLTFNodeIndex parent = state->nodes[roots[0]]->parent; + for (int i = 1; i < roots.size(); ++i) { + if (state->nodes[roots[i]]->parent != parent) { + return FAILED; + } + } + + return OK; +} + +Error GLTFDocument::_create_skeletons(Ref<GLTFState> state) { + for (GLTFSkeletonIndex skel_i = 0; skel_i < state->skeletons.size(); ++skel_i) { + Ref<GLTFSkeleton> gltf_skeleton = state->skeletons.write[skel_i]; + + Skeleton3D *skeleton = memnew(Skeleton3D); + gltf_skeleton->godot_skeleton = skeleton; + + // Make a unique name, no gltf node represents this skeleton + skeleton->set_name(_gen_unique_name(state, "Skeleton3D")); + + List<GLTFNodeIndex> bones; + + for (int i = 0; i < gltf_skeleton->roots.size(); ++i) { + bones.push_back(gltf_skeleton->roots[i]); + } + + // Make the skeleton creation deterministic by going through the roots in + // a sorted order, and DEPTH FIRST + bones.sort(); + + while (!bones.is_empty()) { + const GLTFNodeIndex node_i = bones.front()->get(); + bones.pop_front(); + + Ref<GLTFNode> node = state->nodes[node_i]; + ERR_FAIL_COND_V(node->skeleton != skel_i, FAILED); + + { // Add all child nodes to the stack (deterministically) + Vector<GLTFNodeIndex> child_nodes; + for (int i = 0; i < node->children.size(); ++i) { + const GLTFNodeIndex child_i = node->children[i]; + if (state->nodes[child_i]->skeleton == skel_i) { + child_nodes.push_back(child_i); + } + } + + // Depth first insertion + child_nodes.sort(); + for (int i = child_nodes.size() - 1; i >= 0; --i) { + bones.push_front(child_nodes[i]); + } + } + + const int bone_index = skeleton->get_bone_count(); + + if (node->get_name().is_empty()) { + node->set_name("bone"); + } + + node->set_name(_gen_unique_bone_name(state, skel_i, node->get_name())); + + skeleton->add_bone(node->get_name()); + skeleton->set_bone_rest(bone_index, node->xform); + + if (node->parent >= 0 && state->nodes[node->parent]->skeleton == skel_i) { + const int bone_parent = skeleton->find_bone(state->nodes[node->parent]->get_name()); + ERR_FAIL_COND_V(bone_parent < 0, FAILED); + skeleton->set_bone_parent(bone_index, skeleton->find_bone(state->nodes[node->parent]->get_name())); + } + + state->scene_nodes.insert(node_i, skeleton); + } + } + + ERR_FAIL_COND_V(_map_skin_joints_indices_to_skeleton_bone_indices(state), ERR_PARSE_ERROR); + + return OK; +} + +Error GLTFDocument::_map_skin_joints_indices_to_skeleton_bone_indices(Ref<GLTFState> state) { + for (GLTFSkinIndex skin_i = 0; skin_i < state->skins.size(); ++skin_i) { + Ref<GLTFSkin> skin = state->skins.write[skin_i]; + + Ref<GLTFSkeleton> skeleton = state->skeletons[skin->skeleton]; + + for (int joint_index = 0; joint_index < skin->joints_original.size(); ++joint_index) { + const GLTFNodeIndex node_i = skin->joints_original[joint_index]; + const Ref<GLTFNode> node = state->nodes[node_i]; + + const int bone_index = skeleton->godot_skeleton->find_bone(node->get_name()); + ERR_FAIL_COND_V(bone_index < 0, FAILED); + + skin->joint_i_to_bone_i.insert(joint_index, bone_index); + } + } + + return OK; +} + +Error GLTFDocument::_serialize_skins(Ref<GLTFState> state) { + _remove_duplicate_skins(state); + return OK; +} + +Error GLTFDocument::_create_skins(Ref<GLTFState> state) { + for (GLTFSkinIndex skin_i = 0; skin_i < state->skins.size(); ++skin_i) { + Ref<GLTFSkin> gltf_skin = state->skins.write[skin_i]; + + Ref<Skin> skin; + skin.instance(); + + // Some skins don't have IBM's! What absolute monsters! + const bool has_ibms = !gltf_skin->inverse_binds.is_empty(); + + for (int joint_i = 0; joint_i < gltf_skin->joints_original.size(); ++joint_i) { + GLTFNodeIndex node = gltf_skin->joints_original[joint_i]; + String bone_name = state->nodes[node]->get_name(); + + Transform xform; + if (has_ibms) { + xform = gltf_skin->inverse_binds[joint_i]; + } + + if (state->use_named_skin_binds) { + skin->add_named_bind(bone_name, xform); + } else { + int32_t bone_i = gltf_skin->joint_i_to_bone_i[joint_i]; + skin->add_bind(bone_i, xform); + } + } + + gltf_skin->godot_skin = skin; + } + + // Purge the duplicates! + _remove_duplicate_skins(state); + + // Create unique names now, after removing duplicates + for (GLTFSkinIndex skin_i = 0; skin_i < state->skins.size(); ++skin_i) { + Ref<Skin> skin = state->skins.write[skin_i]->godot_skin; + if (skin->get_name().is_empty()) { + // Make a unique name, no gltf node represents this skin + skin->set_name(_gen_unique_name(state, "Skin")); + } + } + + return OK; +} + +bool GLTFDocument::_skins_are_same(const Ref<Skin> skin_a, const Ref<Skin> skin_b) { + if (skin_a->get_bind_count() != skin_b->get_bind_count()) { + return false; + } + + for (int i = 0; i < skin_a->get_bind_count(); ++i) { + if (skin_a->get_bind_bone(i) != skin_b->get_bind_bone(i)) { + return false; + } + + Transform a_xform = skin_a->get_bind_pose(i); + Transform b_xform = skin_b->get_bind_pose(i); + + if (a_xform != b_xform) { + return false; + } + } + + return true; +} + +void GLTFDocument::_remove_duplicate_skins(Ref<GLTFState> state) { + for (int i = 0; i < state->skins.size(); ++i) { + for (int j = i + 1; j < state->skins.size(); ++j) { + const Ref<Skin> skin_i = state->skins[i]->godot_skin; + const Ref<Skin> skin_j = state->skins[j]->godot_skin; + + if (_skins_are_same(skin_i, skin_j)) { + // replace it and delete the old + state->skins.write[j]->godot_skin = skin_i; + } + } + } +} + +Error GLTFDocument::_serialize_lights(Ref<GLTFState> state) { + Array lights; + for (GLTFLightIndex i = 0; i < state->lights.size(); i++) { + Dictionary d; + Ref<GLTFLight> light = state->lights[i]; + Array color; + color.resize(3); + color[0] = light->color.r; + color[1] = light->color.g; + color[2] = light->color.b; + d["color"] = color; + d["type"] = light->type; + if (light->type == "spot") { + Dictionary s; + float inner_cone_angle = light->inner_cone_angle; + s["innerConeAngle"] = inner_cone_angle; + float outer_cone_angle = light->outer_cone_angle; + s["outerConeAngle"] = outer_cone_angle; + d["spot"] = s; + } + float intensity = light->intensity; + d["intensity"] = intensity; + float range = light->range; + d["range"] = range; + lights.push_back(d); + } + + if (!state->lights.size()) { + return OK; + } + + Dictionary extensions; + if (state->json.has("extensions")) { + extensions = state->json["extensions"]; + } else { + state->json["extensions"] = extensions; + } + Dictionary lights_punctual; + extensions["KHR_lights_punctual"] = lights_punctual; + lights_punctual["lights"] = lights; + + print_verbose("glTF: Total lights: " + itos(state->lights.size())); + + return OK; +} + +Error GLTFDocument::_serialize_cameras(Ref<GLTFState> state) { + Array cameras; + cameras.resize(state->cameras.size()); + for (GLTFCameraIndex i = 0; i < state->cameras.size(); i++) { + Dictionary d; + + Ref<GLTFCamera> camera = state->cameras[i]; + + if (camera->get_perspective() == false) { + Dictionary og; + og["ymag"] = Math::deg2rad(camera->get_fov_size()); + og["xmag"] = Math::deg2rad(camera->get_fov_size()); + og["zfar"] = camera->get_zfar(); + og["znear"] = camera->get_znear(); + d["orthographic"] = og; + d["type"] = "orthographic"; + } else if (camera->get_perspective()) { + Dictionary ppt; + // GLTF spec is in radians, Godot's camera is in degrees. + ppt["yfov"] = Math::deg2rad(camera->get_fov_size()); + ppt["zfar"] = camera->get_zfar(); + ppt["znear"] = camera->get_znear(); + d["perspective"] = ppt; + d["type"] = "perspective"; + } + cameras[i] = d; + } + + if (!state->cameras.size()) { + return OK; + } + + state->json["cameras"] = cameras; + + print_verbose("glTF: Total cameras: " + itos(state->cameras.size())); + + return OK; +} + +Error GLTFDocument::_parse_lights(Ref<GLTFState> state) { + if (!state->json.has("extensions")) { + return OK; + } + Dictionary extensions = state->json["extensions"]; + if (!extensions.has("KHR_lights_punctual")) { + return OK; + } + Dictionary lights_punctual = extensions["KHR_lights_punctual"]; + if (!lights_punctual.has("lights")) { + return OK; + } + + const Array &lights = lights_punctual["lights"]; + + for (GLTFLightIndex light_i = 0; light_i < lights.size(); light_i++) { + const Dictionary &d = lights[light_i]; + + Ref<GLTFLight> light; + light.instance(); + ERR_FAIL_COND_V(!d.has("type"), ERR_PARSE_ERROR); + const String &type = d["type"]; + light->type = type; + + if (d.has("color")) { + const Array &arr = d["color"]; + ERR_FAIL_COND_V(arr.size() != 3, ERR_PARSE_ERROR); + const Color c = Color(arr[0], arr[1], arr[2]).to_srgb(); + light->color = c; + } + if (d.has("intensity")) { + light->intensity = d["intensity"]; + } + if (d.has("range")) { + light->range = d["range"]; + } + if (type == "spot") { + const Dictionary &spot = d["spot"]; + light->inner_cone_angle = spot["innerConeAngle"]; + light->outer_cone_angle = spot["outerConeAngle"]; + ERR_FAIL_COND_V_MSG(light->inner_cone_angle >= light->outer_cone_angle, ERR_PARSE_ERROR, "The inner angle must be smaller than the outer angle."); + } else if (type != "point" && type != "directional") { + ERR_FAIL_V_MSG(ERR_PARSE_ERROR, "Light type is unknown."); + } + + state->lights.push_back(light); + } + + print_verbose("glTF: Total lights: " + itos(state->lights.size())); + + return OK; +} + +Error GLTFDocument::_parse_cameras(Ref<GLTFState> state) { + if (!state->json.has("cameras")) + return OK; + + const Array cameras = state->json["cameras"]; + + for (GLTFCameraIndex i = 0; i < cameras.size(); i++) { + const Dictionary &d = cameras[i]; + + Ref<GLTFCamera> camera; + camera.instance(); + ERR_FAIL_COND_V(!d.has("type"), ERR_PARSE_ERROR); + const String &type = d["type"]; + if (type == "orthographic") { + camera->set_perspective(false); + if (d.has("orthographic")) { + const Dictionary &og = d["orthographic"]; + // GLTF spec is in radians, Godot's camera is in degrees. + camera->set_fov_size(Math::rad2deg(real_t(og["ymag"]))); + camera->set_zfar(og["zfar"]); + camera->set_znear(og["znear"]); + } else { + camera->set_fov_size(10); + } + } else if (type == "perspective") { + camera->set_perspective(true); + if (d.has("perspective")) { + const Dictionary &ppt = d["perspective"]; + // GLTF spec is in radians, Godot's camera is in degrees. + camera->set_fov_size(Math::rad2deg(real_t(ppt["yfov"]))); + camera->set_zfar(ppt["zfar"]); + camera->set_znear(ppt["znear"]); + } else { + camera->set_fov_size(10); + } + } else { + ERR_FAIL_V_MSG(ERR_PARSE_ERROR, "Camera3D should be in 'orthographic' or 'perspective'"); + } + + state->cameras.push_back(camera); + } + + print_verbose("glTF: Total cameras: " + itos(state->cameras.size())); + + return OK; +} + +String GLTFDocument::interpolation_to_string(const GLTFAnimation::Interpolation p_interp) { + String interp = "LINEAR"; + if (p_interp == GLTFAnimation::INTERP_STEP) { + interp = "STEP"; + } else if (p_interp == GLTFAnimation::INTERP_LINEAR) { + interp = "LINEAR"; + } else if (p_interp == GLTFAnimation::INTERP_CATMULLROMSPLINE) { + interp = "CATMULLROMSPLINE"; + } else if (p_interp == GLTFAnimation::INTERP_CUBIC_SPLINE) { + interp = "CUBICSPLINE"; + } + + return interp; +} + +Error GLTFDocument::_serialize_animations(Ref<GLTFState> state) { + if (!state->animation_players.size()) { + return OK; + } + for (int32_t player_i = 0; player_i < state->animation_players.size(); player_i++) { + List<StringName> animation_names; + AnimationPlayer *animation_player = state->animation_players[player_i]; + animation_player->get_animation_list(&animation_names); + if (animation_names.size()) { + for (int animation_name_i = 0; animation_name_i < animation_names.size(); animation_name_i++) { + _convert_animation(state, animation_player, animation_names[animation_name_i]); + } + } + } + Array animations; + for (GLTFAnimationIndex animation_i = 0; animation_i < state->animations.size(); animation_i++) { + Dictionary d; + Ref<GLTFAnimation> gltf_animation = state->animations[animation_i]; + if (!gltf_animation->get_tracks().size()) { + continue; + } + + if (!gltf_animation->get_name().is_empty()) { + d["name"] = gltf_animation->get_name(); + } + Array channels; + Array samplers; + + for (Map<int, GLTFAnimation::Track>::Element *track_i = gltf_animation->get_tracks().front(); track_i; track_i = track_i->next()) { + GLTFAnimation::Track track = track_i->get(); + if (track.translation_track.times.size()) { + Dictionary t; + t["sampler"] = samplers.size(); + Dictionary s; + + s["interpolation"] = interpolation_to_string(track.translation_track.interpolation); + Vector<real_t> times = Variant(track.translation_track.times); + s["input"] = _encode_accessor_as_floats(state, times, false); + Vector<Vector3> values = Variant(track.translation_track.values); + s["output"] = _encode_accessor_as_vec3(state, values, false); + + samplers.push_back(s); + + Dictionary target; + target["path"] = "translation"; + target["node"] = track_i->key(); + + t["target"] = target; + channels.push_back(t); + } + if (track.rotation_track.times.size()) { + Dictionary t; + t["sampler"] = samplers.size(); + Dictionary s; + + s["interpolation"] = interpolation_to_string(track.rotation_track.interpolation); + Vector<real_t> times = Variant(track.rotation_track.times); + s["input"] = _encode_accessor_as_floats(state, times, false); + Vector<Quat> values = track.rotation_track.values; + s["output"] = _encode_accessor_as_quats(state, values, false); + + samplers.push_back(s); + + Dictionary target; + target["path"] = "rotation"; + target["node"] = track_i->key(); + + t["target"] = target; + channels.push_back(t); + } + if (track.scale_track.times.size()) { + Dictionary t; + t["sampler"] = samplers.size(); + Dictionary s; + + s["interpolation"] = interpolation_to_string(track.scale_track.interpolation); + Vector<real_t> times = Variant(track.scale_track.times); + s["input"] = _encode_accessor_as_floats(state, times, false); + Vector<Vector3> values = Variant(track.scale_track.values); + s["output"] = _encode_accessor_as_vec3(state, values, false); + + samplers.push_back(s); + + Dictionary target; + target["path"] = "scale"; + target["node"] = track_i->key(); + + t["target"] = target; + channels.push_back(t); + } + if (track.weight_tracks.size()) { + Dictionary t; + t["sampler"] = samplers.size(); + Dictionary s; + + Vector<real_t> times; + Vector<real_t> values; + + for (int32_t times_i = 0; times_i < track.weight_tracks[0].times.size(); times_i++) { + real_t time = track.weight_tracks[0].times[times_i]; + times.push_back(time); + } + + values.resize(times.size() * track.weight_tracks.size()); + // TODO Sort by order in blend shapes + for (int k = 0; k < track.weight_tracks.size(); k++) { + Vector<float> wdata = track.weight_tracks[k].values; + for (int l = 0; l < wdata.size(); l++) { + values.write[l * track.weight_tracks.size() + k] = wdata.write[l]; + } + } + + s["interpolation"] = interpolation_to_string(track.weight_tracks[track.weight_tracks.size() - 1].interpolation); + s["input"] = _encode_accessor_as_floats(state, times, false); + s["output"] = _encode_accessor_as_floats(state, values, false); + + samplers.push_back(s); + + Dictionary target; + target["path"] = "weights"; + target["node"] = track_i->key(); + + t["target"] = target; + channels.push_back(t); + } + } + if (channels.size() && samplers.size()) { + d["channels"] = channels; + d["samplers"] = samplers; + animations.push_back(d); + } + } + + state->json["animations"] = animations; + + print_verbose("glTF: Total animations '" + itos(state->animations.size()) + "'."); + + return OK; +} + +Error GLTFDocument::_parse_animations(Ref<GLTFState> state) { + if (!state->json.has("animations")) + return OK; + + const Array &animations = state->json["animations"]; + + for (GLTFAnimationIndex i = 0; i < animations.size(); i++) { + const Dictionary &d = animations[i]; + + Ref<GLTFAnimation> animation; + animation.instance(); + + if (!d.has("channels") || !d.has("samplers")) + continue; + + Array channels = d["channels"]; + Array samplers = d["samplers"]; + + if (d.has("name")) { + const String name = d["name"]; + if (name.begins_with("loop") || name.ends_with("loop") || name.begins_with("cycle") || name.ends_with("cycle")) { + animation->set_loop(true); + } + animation->set_name(_sanitize_scene_name(name)); + } + + for (int j = 0; j < channels.size(); j++) { + const Dictionary &c = channels[j]; + if (!c.has("target")) + continue; + + const Dictionary &t = c["target"]; + if (!t.has("node") || !t.has("path")) { + continue; + } + + ERR_FAIL_COND_V(!c.has("sampler"), ERR_PARSE_ERROR); + const int sampler = c["sampler"]; + ERR_FAIL_INDEX_V(sampler, samplers.size(), ERR_PARSE_ERROR); + + GLTFNodeIndex node = t["node"]; + String path = t["path"]; + + ERR_FAIL_INDEX_V(node, state->nodes.size(), ERR_PARSE_ERROR); + + GLTFAnimation::Track *track = nullptr; + + if (!animation->get_tracks().has(node)) { + animation->get_tracks()[node] = GLTFAnimation::Track(); + } + + track = &animation->get_tracks()[node]; + + const Dictionary &s = samplers[sampler]; + + ERR_FAIL_COND_V(!s.has("input"), ERR_PARSE_ERROR); + ERR_FAIL_COND_V(!s.has("output"), ERR_PARSE_ERROR); + + const int input = s["input"]; + const int output = s["output"]; + + GLTFAnimation::Interpolation interp = GLTFAnimation::INTERP_LINEAR; + int output_count = 1; + if (s.has("interpolation")) { + const String &in = s["interpolation"]; + if (in == "STEP") { + interp = GLTFAnimation::INTERP_STEP; + } else if (in == "LINEAR") { + interp = GLTFAnimation::INTERP_LINEAR; + } else if (in == "CATMULLROMSPLINE") { + interp = GLTFAnimation::INTERP_CATMULLROMSPLINE; + output_count = 3; + } else if (in == "CUBICSPLINE") { + interp = GLTFAnimation::INTERP_CUBIC_SPLINE; + output_count = 3; + } + } + + const Vector<float> times = _decode_accessor_as_floats(state, input, false); + if (path == "translation") { + const Vector<Vector3> translations = _decode_accessor_as_vec3(state, output, false); + track->translation_track.interpolation = interp; + track->translation_track.times = Variant(times); //convert via variant + track->translation_track.values = Variant(translations); //convert via variant + } else if (path == "rotation") { + const Vector<Quat> rotations = _decode_accessor_as_quat(state, output, false); + track->rotation_track.interpolation = interp; + track->rotation_track.times = Variant(times); //convert via variant + track->rotation_track.values = rotations; + } else if (path == "scale") { + const Vector<Vector3> scales = _decode_accessor_as_vec3(state, output, false); + track->scale_track.interpolation = interp; + track->scale_track.times = Variant(times); //convert via variant + track->scale_track.values = Variant(scales); //convert via variant + } else if (path == "weights") { + const Vector<float> weights = _decode_accessor_as_floats(state, output, false); + + ERR_FAIL_INDEX_V(state->nodes[node]->mesh, state->meshes.size(), ERR_PARSE_ERROR); + Ref<GLTFMesh> mesh = state->meshes[state->nodes[node]->mesh]; + ERR_CONTINUE(!mesh->get_blend_weights().size()); + const int wc = mesh->get_blend_weights().size(); + + track->weight_tracks.resize(wc); + + const int expected_value_count = times.size() * output_count * wc; + ERR_FAIL_COND_V_MSG(weights.size() != expected_value_count, ERR_PARSE_ERROR, "Invalid weight data, expected " + itos(expected_value_count) + " weight values, got " + itos(weights.size()) + " instead."); + + const int wlen = weights.size() / wc; + for (int k = 0; k < wc; k++) { //separate tracks, having them together is not such a good idea + GLTFAnimation::Channel<float> cf; + cf.interpolation = interp; + cf.times = Variant(times); + Vector<float> wdata; + wdata.resize(wlen); + for (int l = 0; l < wlen; l++) { + wdata.write[l] = weights[l * wc + k]; + } + + cf.values = wdata; + track->weight_tracks.write[k] = cf; + } + } else { + WARN_PRINT("Invalid path '" + path + "'."); + } + } + + state->animations.push_back(animation); + } + + print_verbose("glTF: Total animations '" + itos(state->animations.size()) + "'."); + + return OK; +} + +void GLTFDocument::_assign_scene_names(Ref<GLTFState> state) { + for (int i = 0; i < state->nodes.size(); i++) { + Ref<GLTFNode> n = state->nodes[i]; + + // Any joints get unique names generated when the skeleton is made, unique to the skeleton + if (n->skeleton >= 0) + continue; + + if (n->get_name().is_empty()) { + if (n->mesh >= 0) { + n->set_name(_gen_unique_name(state, "Mesh")); + } else if (n->camera >= 0) { + n->set_name(_gen_unique_name(state, "Camera3D")); + } else { + n->set_name(_gen_unique_name(state, "Node")); + } + } + + n->set_name(_gen_unique_name(state, n->get_name())); + } +} + +BoneAttachment3D *GLTFDocument::_generate_bone_attachment(Ref<GLTFState> state, Skeleton3D *skeleton, const GLTFNodeIndex node_index) { + Ref<GLTFNode> gltf_node = state->nodes[node_index]; + Ref<GLTFNode> bone_node = state->nodes[gltf_node->parent]; + + BoneAttachment3D *bone_attachment = memnew(BoneAttachment3D); + print_verbose("glTF: Creating bone attachment for: " + gltf_node->get_name()); + + ERR_FAIL_COND_V(!bone_node->joint, nullptr); + + bone_attachment->set_bone_name(bone_node->get_name()); + + return bone_attachment; +} + +GLTFMeshIndex GLTFDocument::_convert_mesh_instance(Ref<GLTFState> state, MeshInstance3D *p_mesh_instance) { + ERR_FAIL_NULL_V(p_mesh_instance, -1); + if (p_mesh_instance->get_mesh().is_null()) { + return -1; + } + Ref<EditorSceneImporterMesh> import_mesh; + import_mesh.instance(); + Ref<Mesh> godot_mesh = p_mesh_instance->get_mesh(); + if (godot_mesh.is_null()) { + return -1; + } + Vector<float> blend_weights; + Vector<String> blend_names; + int32_t blend_count = godot_mesh->get_blend_shape_count(); + blend_names.resize(blend_count); + blend_weights.resize(blend_count); + for (int32_t blend_i = 0; blend_i < godot_mesh->get_blend_shape_count(); blend_i++) { + String blend_name = godot_mesh->get_blend_shape_name(blend_i); + blend_names.write[blend_i] = blend_name; + import_mesh->add_blend_shape(blend_name); + } + for (int32_t surface_i = 0; surface_i < godot_mesh->get_surface_count(); surface_i++) { + Mesh::PrimitiveType primitive_type = godot_mesh->surface_get_primitive_type(surface_i); + Array arrays = godot_mesh->surface_get_arrays(surface_i); + Array blend_shape_arrays = godot_mesh->surface_get_blend_shape_arrays(surface_i); + Ref<Material> mat = godot_mesh->surface_get_material(surface_i); + Ref<ArrayMesh> godot_array_mesh = godot_mesh; + String surface_name; + if (godot_array_mesh.is_valid()) { + surface_name = godot_array_mesh->surface_get_name(surface_i); + } + if (p_mesh_instance->get_surface_material(surface_i).is_valid()) { + mat = p_mesh_instance->get_surface_material(surface_i); + } + if (p_mesh_instance->get_material_override().is_valid()) { + mat = p_mesh_instance->get_material_override(); + } + import_mesh->add_surface(primitive_type, arrays, blend_shape_arrays, Dictionary(), mat, surface_name); + } + for (int32_t blend_i = 0; blend_i < blend_count; blend_i++) { + blend_weights.write[blend_i] = 0.0f; + } + Ref<GLTFMesh> gltf_mesh; + gltf_mesh.instance(); + gltf_mesh->set_mesh(import_mesh); + gltf_mesh->set_blend_weights(blend_weights); + GLTFMeshIndex mesh_i = state->meshes.size(); + state->meshes.push_back(gltf_mesh); + return mesh_i; +} + +EditorSceneImporterMeshNode3D *GLTFDocument::_generate_mesh_instance(Ref<GLTFState> state, Node *scene_parent, const GLTFNodeIndex node_index) { + Ref<GLTFNode> gltf_node = state->nodes[node_index]; + + ERR_FAIL_INDEX_V(gltf_node->mesh, state->meshes.size(), nullptr); + + EditorSceneImporterMeshNode3D *mi = memnew(EditorSceneImporterMeshNode3D); + print_verbose("glTF: Creating mesh for: " + gltf_node->get_name()); + + Ref<GLTFMesh> mesh = state->meshes.write[gltf_node->mesh]; + if (mesh.is_null()) { + return mi; + } + Ref<EditorSceneImporterMesh> import_mesh = mesh->get_mesh(); + if (import_mesh.is_null()) { + return mi; + } + mi->set_mesh(import_mesh); + for (int i = 0; i < mesh->get_blend_weights().size(); i++) { + mi->set("blend_shapes/" + mesh->get_mesh()->get_blend_shape_name(i), mesh->get_blend_weights()[i]); + } + return mi; +} + +Light3D *GLTFDocument::_generate_light(Ref<GLTFState> state, Node *scene_parent, const GLTFNodeIndex node_index) { + Ref<GLTFNode> gltf_node = state->nodes[node_index]; + + ERR_FAIL_INDEX_V(gltf_node->light, state->lights.size(), nullptr); + + print_verbose("glTF: Creating light for: " + gltf_node->get_name()); + + Ref<GLTFLight> l = state->lights[gltf_node->light]; + + float intensity = l->intensity; + if (intensity > 10) { + // GLTF spec has the default around 1, but Blender defaults lights to 100. + // The only sane way to handle this is to check where it came from and + // handle it accordingly. If it's over 10, it probably came from Blender. + intensity /= 100; + } + + if (l->type == "directional") { + DirectionalLight3D *light = memnew(DirectionalLight3D); + light->set_param(Light3D::PARAM_ENERGY, intensity); + light->set_color(l->color); + return light; + } + + const float range = CLAMP(l->range, 0, 4096); + // Doubling the range will double the effective brightness, so we need double attenuation (half brightness). + // We want to have double intensity give double brightness, so we need half the attenuation. + const float attenuation = range / intensity; + if (l->type == "point") { + OmniLight3D *light = memnew(OmniLight3D); + light->set_param(OmniLight3D::PARAM_ATTENUATION, attenuation); + light->set_param(OmniLight3D::PARAM_RANGE, range); + light->set_color(l->color); + return light; + } + if (l->type == "spot") { + SpotLight3D *light = memnew(SpotLight3D); + light->set_param(SpotLight3D::PARAM_ATTENUATION, attenuation); + light->set_param(SpotLight3D::PARAM_RANGE, range); + light->set_param(SpotLight3D::PARAM_SPOT_ANGLE, Math::rad2deg(l->outer_cone_angle)); + light->set_color(l->color); + + // Line of best fit derived from guessing, see https://www.desmos.com/calculator/biiflubp8b + // The points in desmos are not exact, except for (1, infinity). + float angle_ratio = l->inner_cone_angle / l->outer_cone_angle; + float angle_attenuation = 0.2 / (1 - angle_ratio) - 0.1; + light->set_param(SpotLight3D::PARAM_SPOT_ATTENUATION, angle_attenuation); + return light; + } + return nullptr; +} + +Camera3D *GLTFDocument::_generate_camera(Ref<GLTFState> state, Node *scene_parent, const GLTFNodeIndex node_index) { + Ref<GLTFNode> gltf_node = state->nodes[node_index]; + + ERR_FAIL_INDEX_V(gltf_node->camera, state->cameras.size(), nullptr); + + Camera3D *camera = memnew(Camera3D); + print_verbose("glTF: Creating camera for: " + gltf_node->get_name()); + + Ref<GLTFCamera> c = state->cameras[gltf_node->camera]; + if (c->get_perspective()) { + camera->set_perspective(c->get_fov_size(), c->get_znear(), c->get_zfar()); + } else { + camera->set_orthogonal(c->get_fov_size(), c->get_znear(), c->get_zfar()); + } + + return camera; +} + +GLTFCameraIndex GLTFDocument::_convert_camera(Ref<GLTFState> state, Camera3D *p_camera) { + print_verbose("glTF: Converting camera: " + p_camera->get_name()); + + Ref<GLTFCamera> c; + c.instance(); + + if (p_camera->get_projection() == Camera3D::Projection::PROJECTION_PERSPECTIVE) { + c->set_perspective(true); + c->set_fov_size(p_camera->get_fov()); + c->set_zfar(p_camera->get_far()); + c->set_znear(p_camera->get_near()); + } else { + c->set_fov_size(p_camera->get_fov()); + c->set_zfar(p_camera->get_far()); + c->set_znear(p_camera->get_near()); + } + GLTFCameraIndex camera_index = state->cameras.size(); + state->cameras.push_back(c); + return camera_index; +} + +GLTFLightIndex GLTFDocument::_convert_light(Ref<GLTFState> state, Light3D *p_light) { + print_verbose("glTF: Converting light: " + p_light->get_name()); + + Ref<GLTFLight> l; + l.instance(); + l->color = p_light->get_color(); + if (cast_to<DirectionalLight3D>(p_light)) { + l->type = "directional"; + DirectionalLight3D *light = cast_to<DirectionalLight3D>(p_light); + l->intensity = light->get_param(DirectionalLight3D::PARAM_ENERGY); + l->range = FLT_MAX; // Range for directional lights is infinite in Godot. + } else if (cast_to<OmniLight3D>(p_light)) { + l->type = "point"; + OmniLight3D *light = cast_to<OmniLight3D>(p_light); + l->range = light->get_param(OmniLight3D::PARAM_RANGE); + float attenuation = p_light->get_param(OmniLight3D::PARAM_ATTENUATION); + l->intensity = l->range / attenuation; + } else if (cast_to<SpotLight3D>(p_light)) { + l->type = "spot"; + SpotLight3D *light = cast_to<SpotLight3D>(p_light); + l->range = light->get_param(SpotLight3D::PARAM_RANGE); + float attenuation = light->get_param(SpotLight3D::PARAM_ATTENUATION); + l->intensity = l->range / attenuation; + l->outer_cone_angle = Math::deg2rad(light->get_param(SpotLight3D::PARAM_SPOT_ANGLE)); + + // This equation is the inverse of the import equation (which has a desmos link). + float angle_ratio = 1 - (0.2 / (0.1 + light->get_param(SpotLight3D::PARAM_SPOT_ATTENUATION))); + angle_ratio = MAX(0, angle_ratio); + l->inner_cone_angle = l->outer_cone_angle * angle_ratio; + } + + GLTFLightIndex light_index = state->lights.size(); + state->lights.push_back(l); + return light_index; +} + +GLTFSkeletonIndex GLTFDocument::_convert_skeleton(Ref<GLTFState> state, Skeleton3D *p_skeleton) { + print_verbose("glTF: Converting skeleton: " + p_skeleton->get_name()); + Ref<GLTFSkeleton> gltf_skeleton; + gltf_skeleton.instance(); + gltf_skeleton->set_name(_gen_unique_name(state, p_skeleton->get_name())); + gltf_skeleton->godot_skeleton = p_skeleton; + GLTFSkeletonIndex skeleton_i = state->skeletons.size(); + state->skeletons.push_back(gltf_skeleton); + return skeleton_i; +} + +void GLTFDocument::_convert_spatial(Ref<GLTFState> state, Node3D *p_spatial, Ref<GLTFNode> p_node) { + Transform xform = p_spatial->get_transform(); + p_node->scale = xform.basis.get_scale(); + p_node->rotation = xform.basis.get_rotation_quat(); + p_node->translation = xform.origin; +} + +Node3D *GLTFDocument::_generate_spatial(Ref<GLTFState> state, Node *scene_parent, const GLTFNodeIndex node_index) { + Ref<GLTFNode> gltf_node = state->nodes[node_index]; + + Node3D *spatial = memnew(Node3D); + print_verbose("glTF: Converting spatial: " + gltf_node->get_name()); + + return spatial; +} +void GLTFDocument::_convert_scene_node(Ref<GLTFState> state, Node *p_current, Node *p_root, const GLTFNodeIndex p_gltf_parent, const GLTFNodeIndex p_gltf_root) { + bool retflag = true; + Node3D *spatial = cast_to<Node3D>(p_current); + _check_visibility(p_current, retflag); + if (retflag) { + return; + } + Ref<GLTFNode> gltf_node; + gltf_node.instance(); + gltf_node->set_name(_gen_unique_name(state, p_current->get_name())); + if (cast_to<Node3D>(p_current)) { + _convert_spatial(state, spatial, gltf_node); + } + if (cast_to<MeshInstance3D>(p_current)) { + _convert_mesh_to_gltf(p_current, state, spatial, gltf_node); + } else if (cast_to<BoneAttachment3D>(p_current)) { + _convert_bone_attachment_to_gltf(p_current, state, gltf_node, retflag); + // TODO 2020-12-21 iFire Handle the case of objects under the bone attachment. + return; + } else if (cast_to<Skeleton3D>(p_current)) { + _convert_skeleton_to_gltf(p_current, state, p_gltf_parent, p_gltf_root, gltf_node, p_root); + // We ignore the Godot Engine node that is the skeleton. + return; + } else if (cast_to<MultiMeshInstance3D>(p_current)) { + _convert_mult_mesh_instance_to_gltf(p_current, p_gltf_parent, p_gltf_root, gltf_node, state, p_root); + } else if (cast_to<CSGShape3D>(p_current)) { + if (p_current->get_parent() && cast_to<CSGShape3D>(p_current)->is_root_shape()) { + _convert_csg_shape_to_gltf(p_current, p_gltf_parent, gltf_node, state); + } + } else if (cast_to<GridMap>(p_current)) { + _convert_grid_map_to_gltf(p_current, p_gltf_parent, p_gltf_root, gltf_node, state, p_root); + } else if (cast_to<Camera3D>(p_current)) { + Camera3D *camera = Object::cast_to<Camera3D>(p_current); + _convert_camera_to_gltf(camera, state, spatial, gltf_node); + } else if (cast_to<Light3D>(p_current)) { + Light3D *light = Object::cast_to<Light3D>(p_current); + _convert_light_to_gltf(light, state, spatial, gltf_node); + } else if (cast_to<AnimationPlayer>(p_current)) { + AnimationPlayer *animation_player = Object::cast_to<AnimationPlayer>(p_current); + _convert_animation_player_to_gltf(animation_player, state, p_gltf_parent, p_gltf_root, gltf_node, p_current, p_root); + } + GLTFNodeIndex current_node_i = state->nodes.size(); + GLTFNodeIndex gltf_root = p_gltf_root; + if (gltf_root == -1) { + gltf_root = current_node_i; + Array scenes; + scenes.push_back(gltf_root); + state->json["scene"] = scenes; + } + _create_gltf_node(state, p_current, current_node_i, p_gltf_parent, gltf_root, gltf_node); + for (int node_i = 0; node_i < p_current->get_child_count(); node_i++) { + _convert_scene_node(state, p_current->get_child(node_i), p_root, current_node_i, gltf_root); + } +} + +void GLTFDocument::_convert_csg_shape_to_gltf(Node *p_current, GLTFNodeIndex p_gltf_parent, Ref<GLTFNode> gltf_node, Ref<GLTFState> state) { + CSGShape3D *csg = Object::cast_to<CSGShape3D>(p_current); + csg->call("_update_shape"); + Array meshes = csg->get_meshes(); + if (meshes.size() != 2) { + return; + } + Ref<Material> mat; + if (csg->get_material_override().is_valid()) { + mat = csg->get_material_override(); + } + Ref<GLTFMesh> gltf_mesh; + gltf_mesh.instance(); + Ref<EditorSceneImporterMesh> import_mesh; + import_mesh.instance(); + Ref<ArrayMesh> array_mesh = csg->get_meshes()[1]; + for (int32_t surface_i = 0; surface_i < array_mesh->get_surface_count(); surface_i++) { + import_mesh->add_surface(Mesh::PrimitiveType::PRIMITIVE_TRIANGLES, array_mesh->surface_get_arrays(surface_i), Array(), Dictionary(), mat, array_mesh->surface_get_name(surface_i)); + } + gltf_mesh->set_mesh(import_mesh); + GLTFMeshIndex mesh_i = state->meshes.size(); + state->meshes.push_back(gltf_mesh); + gltf_node->mesh = mesh_i; + gltf_node->xform = csg->get_meshes()[0]; + gltf_node->set_name(_gen_unique_name(state, csg->get_name())); +} + +void GLTFDocument::_create_gltf_node(Ref<GLTFState> state, Node *p_scene_parent, GLTFNodeIndex current_node_i, + GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_gltf_node, Ref<GLTFNode> gltf_node) { + state->scene_nodes.insert(current_node_i, p_scene_parent); + state->nodes.push_back(gltf_node); + if (current_node_i == p_parent_node_index) { + return; + } + if (p_parent_node_index == -1) { + return; + } + state->nodes.write[p_parent_node_index]->children.push_back(current_node_i); +} + +void GLTFDocument::_convert_animation_player_to_gltf(AnimationPlayer *animation_player, Ref<GLTFState> state, const GLTFNodeIndex &p_gltf_current, const GLTFNodeIndex &p_gltf_root_index, Ref<GLTFNode> p_gltf_node, Node *p_scene_parent, Node *p_root) { + ERR_FAIL_COND(!animation_player); + state->animation_players.push_back(animation_player); + print_verbose(String("glTF: Converting animation player: ") + animation_player->get_name()); +} + +void GLTFDocument::_check_visibility(Node *p_node, bool &retflag) { + retflag = true; + Node3D *spatial = Object::cast_to<Node3D>(p_node); + Node2D *node_2d = Object::cast_to<Node2D>(p_node); + if (node_2d && !node_2d->is_visible()) { + return; + } + if (spatial && !spatial->is_visible()) { + return; + } + retflag = false; +} + +void GLTFDocument::_convert_camera_to_gltf(Camera3D *camera, Ref<GLTFState> state, Node3D *spatial, Ref<GLTFNode> gltf_node) { + ERR_FAIL_COND(!camera); + GLTFCameraIndex camera_index = _convert_camera(state, camera); + if (camera_index != -1) { + gltf_node->camera = camera_index; + } +} + +void GLTFDocument::_convert_light_to_gltf(Light3D *light, Ref<GLTFState> state, Node3D *spatial, Ref<GLTFNode> gltf_node) { + ERR_FAIL_COND(!light); + GLTFLightIndex light_index = _convert_light(state, light); + if (light_index != -1) { + gltf_node->light = light_index; + } +} + +void GLTFDocument::_convert_grid_map_to_gltf(Node *p_scene_parent, const GLTFNodeIndex &p_parent_node_index, const GLTFNodeIndex &p_root_node_index, Ref<GLTFNode> gltf_node, Ref<GLTFState> state, Node *p_root_node) { + GridMap *grid_map = Object::cast_to<GridMap>(p_scene_parent); + ERR_FAIL_COND(!grid_map); + Array cells = grid_map->get_used_cells(); + for (int32_t k = 0; k < cells.size(); k++) { + GLTFNode *new_gltf_node = memnew(GLTFNode); + gltf_node->children.push_back(state->nodes.size()); + state->nodes.push_back(new_gltf_node); + Vector3 cell_location = cells[k]; + int32_t cell = grid_map->get_cell_item( + Vector3(cell_location.x, cell_location.y, cell_location.z)); + EditorSceneImporterMeshNode3D *import_mesh_node = memnew(EditorSceneImporterMeshNode3D); + import_mesh_node->set_mesh(grid_map->get_mesh_library()->get_item_mesh(cell)); + Transform cell_xform; + cell_xform.basis.set_orthogonal_index( + grid_map->get_cell_item_orientation( + Vector3(cell_location.x, cell_location.y, cell_location.z))); + cell_xform.basis.scale(Vector3(grid_map->get_cell_scale(), + grid_map->get_cell_scale(), + grid_map->get_cell_scale())); + cell_xform.set_origin(grid_map->map_to_world( + Vector3(cell_location.x, cell_location.y, cell_location.z))); + Ref<GLTFMesh> gltf_mesh; + gltf_mesh.instance(); + gltf_mesh = import_mesh_node; + new_gltf_node->mesh = state->meshes.size(); + state->meshes.push_back(gltf_mesh); + new_gltf_node->xform = cell_xform * grid_map->get_transform(); + new_gltf_node->set_name(_gen_unique_name(state, grid_map->get_mesh_library()->get_item_name(cell))); + } +} + +void GLTFDocument::_convert_mult_mesh_instance_to_gltf(Node *p_scene_parent, const GLTFNodeIndex &p_parent_node_index, const GLTFNodeIndex &p_root_node_index, Ref<GLTFNode> gltf_node, Ref<GLTFState> state, Node *p_root_node) { + MultiMeshInstance3D *multi_mesh_instance = Object::cast_to<MultiMeshInstance3D>(p_scene_parent); + ERR_FAIL_COND(!multi_mesh_instance); + Ref<MultiMesh> multi_mesh = multi_mesh_instance->get_multimesh(); + if (multi_mesh.is_valid()) { + for (int32_t instance_i = 0; instance_i < multi_mesh->get_instance_count(); + instance_i++) { + GLTFNode *new_gltf_node = memnew(GLTFNode); + Transform transform; + if (multi_mesh->get_transform_format() == MultiMesh::TRANSFORM_2D) { + Transform2D xform_2d = multi_mesh->get_instance_transform_2d(instance_i); + transform.origin = + Vector3(xform_2d.get_origin().x, 0, xform_2d.get_origin().y); + real_t rotation = xform_2d.get_rotation(); + Quat quat; + quat.set_axis_angle(Vector3(0, 1, 0), rotation); + Size2 scale = xform_2d.get_scale(); + transform.basis.set_quat_scale(quat, + Vector3(scale.x, 0, scale.y)); + transform = + multi_mesh_instance->get_transform() * transform; + } else if (multi_mesh->get_transform_format() == MultiMesh::TRANSFORM_3D) { + transform = multi_mesh_instance->get_transform() * + multi_mesh->get_instance_transform(instance_i); + } + Ref<ArrayMesh> mm = multi_mesh->get_mesh(); + if (mm.is_valid()) { + Ref<EditorSceneImporterMesh> mesh; + mesh.instance(); + for (int32_t surface_i = 0; surface_i < mm->get_surface_count(); surface_i++) { + Array surface = mm->surface_get_arrays(surface_i); + mesh->add_surface(mm->surface_get_primitive_type(surface_i), surface, Array(), Dictionary(), + mm->surface_get_material(surface_i), mm->get_name()); + } + Ref<GLTFMesh> gltf_mesh; + gltf_mesh.instance(); + gltf_mesh->set_name(multi_mesh->get_name()); + gltf_mesh->set_mesh(mesh); + new_gltf_node->mesh = state->meshes.size(); + state->meshes.push_back(gltf_mesh); + } + new_gltf_node->xform = transform; + new_gltf_node->set_name(_gen_unique_name(state, multi_mesh_instance->get_name())); + gltf_node->children.push_back(state->nodes.size()); + state->nodes.push_back(new_gltf_node); + } + } +} + +void GLTFDocument::_convert_skeleton_to_gltf(Node *p_scene_parent, Ref<GLTFState> state, const GLTFNodeIndex &p_parent_node_index, const GLTFNodeIndex &p_root_node_index, Ref<GLTFNode> gltf_node, Node *p_root_node) { + Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_scene_parent); + if (skeleton) { + // Remove placeholder skeleton3d node by not creating the gltf node + // Skins are per mesh + for (int node_i = 0; node_i < skeleton->get_child_count(); node_i++) { + _convert_scene_node(state, skeleton->get_child(node_i), p_root_node, p_parent_node_index, p_root_node_index); + } + } +} + +void GLTFDocument::_convert_bone_attachment_to_gltf(Node *p_scene_parent, Ref<GLTFState> state, Ref<GLTFNode> gltf_node, bool &retflag) { + retflag = true; + BoneAttachment3D *bone_attachment = Object::cast_to<BoneAttachment3D>(p_scene_parent); + if (bone_attachment) { + Node *node = bone_attachment->get_parent(); + while (node) { + Skeleton3D *bone_attachment_skeleton = Object::cast_to<Skeleton3D>(node); + if (bone_attachment_skeleton) { + for (GLTFSkeletonIndex skeleton_i = 0; skeleton_i < state->skeletons.size(); skeleton_i++) { + if (state->skeletons[skeleton_i]->godot_skeleton != bone_attachment_skeleton) { + continue; + } + state->skeletons.write[skeleton_i]->bone_attachments.push_back(bone_attachment); + break; + } + break; + } + node = node->get_parent(); + } + gltf_node.unref(); + return; + } + retflag = false; +} + +void GLTFDocument::_convert_mesh_to_gltf(Node *p_scene_parent, Ref<GLTFState> state, Node3D *spatial, Ref<GLTFNode> gltf_node) { + MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_scene_parent); + if (mi) { + GLTFMeshIndex gltf_mesh_index = _convert_mesh_instance(state, mi); + if (gltf_mesh_index != -1) { + gltf_node->mesh = gltf_mesh_index; + } + } +} + +void GLTFDocument::_generate_scene_node(Ref<GLTFState> state, Node *scene_parent, Node3D *scene_root, const GLTFNodeIndex node_index) { + Ref<GLTFNode> gltf_node = state->nodes[node_index]; + + Node3D *current_node = nullptr; + + // Is our parent a skeleton + Skeleton3D *active_skeleton = Object::cast_to<Skeleton3D>(scene_parent); + + if (gltf_node->skeleton >= 0) { + Skeleton3D *skeleton = state->skeletons[gltf_node->skeleton]->godot_skeleton; + + if (active_skeleton != skeleton) { + ERR_FAIL_COND_MSG(active_skeleton != nullptr, "glTF: Generating scene detected direct parented Skeletons"); + + // Add it to the scene if it has not already been added + if (skeleton->get_parent() == nullptr) { + scene_parent->add_child(skeleton); + skeleton->set_owner(scene_root); + } + } + + active_skeleton = skeleton; + current_node = skeleton; + } + + // If we have an active skeleton, and the node is node skinned, we need to create a bone attachment + if (current_node == nullptr && active_skeleton != nullptr && gltf_node->skin < 0) { + BoneAttachment3D *bone_attachment = _generate_bone_attachment(state, active_skeleton, node_index); + + scene_parent->add_child(bone_attachment); + bone_attachment->set_owner(scene_root); + + // There is no gltf_node that represent this, so just directly create a unique name + bone_attachment->set_name(_gen_unique_name(state, "BoneAttachment3D")); + + // We change the scene_parent to our bone attachment now. We do not set current_node because we want to make the node + // and attach it to the bone_attachment + scene_parent = bone_attachment; + } + + // We still have not managed to make a node + if (current_node == nullptr) { + if (gltf_node->mesh >= 0) { + current_node = _generate_mesh_instance(state, scene_parent, node_index); + } else if (gltf_node->camera >= 0) { + current_node = _generate_camera(state, scene_parent, node_index); + } else if (gltf_node->light >= 0) { + current_node = _generate_light(state, scene_parent, node_index); + } + + if (!current_node) { + current_node = _generate_spatial(state, scene_parent, node_index); + } + + scene_parent->add_child(current_node); + if (current_node != scene_root) { + current_node->set_owner(scene_root); + } + current_node->set_transform(gltf_node->xform); + current_node->set_name(gltf_node->get_name()); + } + + state->scene_nodes.insert(node_index, current_node); + + for (int i = 0; i < gltf_node->children.size(); ++i) { + _generate_scene_node(state, current_node, scene_root, gltf_node->children[i]); + } +} + +template <class T> +struct EditorSceneImporterGLTFInterpolate { + T lerp(const T &a, const T &b, float c) const { + return a + (b - a) * c; + } + + T catmull_rom(const T &p0, const T &p1, const T &p2, const T &p3, float t) { + const float t2 = t * t; + const float t3 = t2 * t; + + return 0.5f * ((2.0f * p1) + (-p0 + p2) * t + (2.0f * p0 - 5.0f * p1 + 4.0f * p2 - p3) * t2 + (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3); + } + + T bezier(T start, T control_1, T control_2, T end, float t) { + /* Formula from Wikipedia article on Bezier curves. */ + const real_t omt = (1.0 - t); + const real_t omt2 = omt * omt; + const real_t omt3 = omt2 * omt; + const real_t t2 = t * t; + const real_t t3 = t2 * t; + + return start * omt3 + control_1 * omt2 * t * 3.0 + control_2 * omt * t2 * 3.0 + end * t3; + } +}; + +// thank you for existing, partial specialization +template <> +struct EditorSceneImporterGLTFInterpolate<Quat> { + Quat lerp(const Quat &a, const Quat &b, const float c) const { + ERR_FAIL_COND_V_MSG(!a.is_normalized(), Quat(), "The quaternion \"a\" must be normalized."); + ERR_FAIL_COND_V_MSG(!b.is_normalized(), Quat(), "The quaternion \"b\" must be normalized."); + + return a.slerp(b, c).normalized(); + } + + Quat catmull_rom(const Quat &p0, const Quat &p1, const Quat &p2, const Quat &p3, const float c) { + ERR_FAIL_COND_V_MSG(!p1.is_normalized(), Quat(), "The quaternion \"p1\" must be normalized."); + ERR_FAIL_COND_V_MSG(!p2.is_normalized(), Quat(), "The quaternion \"p2\" must be normalized."); + + return p1.slerp(p2, c).normalized(); + } + + Quat bezier(const Quat start, const Quat control_1, const Quat control_2, const Quat end, const float t) { + ERR_FAIL_COND_V_MSG(!start.is_normalized(), Quat(), "The start quaternion must be normalized."); + ERR_FAIL_COND_V_MSG(!end.is_normalized(), Quat(), "The end quaternion must be normalized."); + + return start.slerp(end, t).normalized(); + } +}; + +template <class T> +T GLTFDocument::_interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, const float p_time, const GLTFAnimation::Interpolation p_interp) { + //could use binary search, worth it? + int idx = -1; + for (int i = 0; i < p_times.size(); i++) { + if (p_times[i] > p_time) + break; + idx++; + } + + EditorSceneImporterGLTFInterpolate<T> interp; + + switch (p_interp) { + case GLTFAnimation::INTERP_LINEAR: { + if (idx == -1) { + return p_values[0]; + } else if (idx >= p_times.size() - 1) { + return p_values[p_times.size() - 1]; + } + + const float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]); + + return interp.lerp(p_values[idx], p_values[idx + 1], c); + } break; + case GLTFAnimation::INTERP_STEP: { + if (idx == -1) { + return p_values[0]; + } else if (idx >= p_times.size() - 1) { + return p_values[p_times.size() - 1]; + } + + return p_values[idx]; + } break; + case GLTFAnimation::INTERP_CATMULLROMSPLINE: { + if (idx == -1) { + return p_values[1]; + } else if (idx >= p_times.size() - 1) { + return p_values[1 + p_times.size() - 1]; + } + + const float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]); + + return interp.catmull_rom(p_values[idx - 1], p_values[idx], p_values[idx + 1], p_values[idx + 3], c); + } break; + case GLTFAnimation::INTERP_CUBIC_SPLINE: { + if (idx == -1) { + return p_values[1]; + } else if (idx >= p_times.size() - 1) { + return p_values[(p_times.size() - 1) * 3 + 1]; + } + + const float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]); + + const T from = p_values[idx * 3 + 1]; + const T c1 = from + p_values[idx * 3 + 2]; + const T to = p_values[idx * 3 + 4]; + const T c2 = to + p_values[idx * 3 + 3]; + + return interp.bezier(from, c1, c2, to, c); + } break; + } + + ERR_FAIL_V(p_values[0]); +} + +void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap, const GLTFAnimationIndex index, const int bake_fps) { + Ref<GLTFAnimation> anim = state->animations[index]; + + String name = anim->get_name(); + if (name.is_empty()) { + // No node represent these, and they are not in the hierarchy, so just make a unique name + name = _gen_unique_name(state, "Animation"); + } + + Ref<Animation> animation; + animation.instance(); + animation->set_name(name); + + if (anim->get_loop()) { + animation->set_loop(true); + } + + float length = 0; + + for (Map<int, GLTFAnimation::Track>::Element *track_i = anim->get_tracks().front(); track_i; track_i = track_i->next()) { + const GLTFAnimation::Track &track = track_i->get(); + //need to find the path + NodePath node_path; + + GLTFNodeIndex node_index = track_i->key(); + if (state->nodes[node_index]->fake_joint_parent >= 0) { + // Should be same as parent + node_index = state->nodes[node_index]->fake_joint_parent; + } + + const Ref<GLTFNode> gltf_node = state->nodes[track_i->key()]; + + if (gltf_node->skeleton >= 0) { + const Skeleton3D *sk = Object::cast_to<Skeleton3D>(state->scene_nodes.find(node_index)->get()); + ERR_FAIL_COND(sk == nullptr); + + const String path = ap->get_parent()->get_path_to(sk); + const String bone = gltf_node->get_name(); + node_path = path + ":" + bone; + } else { + Node *root = ap->get_parent(); + Node *godot_node = state->scene_nodes.find(node_index)->get(); + node_path = root->get_path_to(godot_node); + } + + for (int i = 0; i < track.rotation_track.times.size(); i++) { + length = MAX(length, track.rotation_track.times[i]); + } + for (int i = 0; i < track.translation_track.times.size(); i++) { + length = MAX(length, track.translation_track.times[i]); + } + for (int i = 0; i < track.scale_track.times.size(); i++) { + length = MAX(length, track.scale_track.times[i]); + } + + for (int i = 0; i < track.weight_tracks.size(); i++) { + for (int j = 0; j < track.weight_tracks[i].times.size(); j++) { + length = MAX(length, track.weight_tracks[i].times[j]); + } + } + + if (track.rotation_track.values.size() || track.translation_track.values.size() || track.scale_track.values.size()) { + //make transform track + int track_idx = animation->get_track_count(); + animation->add_track(Animation::TYPE_TRANSFORM); + animation->track_set_path(track_idx, node_path); + //first determine animation length + + const float increment = 1.0 / float(bake_fps); + float time = 0.0; + + Vector3 base_pos; + Quat base_rot; + Vector3 base_scale = Vector3(1, 1, 1); + + if (!track.rotation_track.values.size()) { + base_rot = state->nodes[track_i->key()]->rotation.normalized(); + } + + if (!track.translation_track.values.size()) { + base_pos = state->nodes[track_i->key()]->translation; + } + + if (!track.scale_track.values.size()) { + base_scale = state->nodes[track_i->key()]->scale; + } + + bool last = false; + while (true) { + Vector3 pos = base_pos; + Quat rot = base_rot; + Vector3 scale = base_scale; + + if (track.translation_track.times.size()) { + pos = _interpolate_track<Vector3>(track.translation_track.times, track.translation_track.values, time, track.translation_track.interpolation); + } + + if (track.rotation_track.times.size()) { + rot = _interpolate_track<Quat>(track.rotation_track.times, track.rotation_track.values, time, track.rotation_track.interpolation); + } + + if (track.scale_track.times.size()) { + scale = _interpolate_track<Vector3>(track.scale_track.times, track.scale_track.values, time, track.scale_track.interpolation); + } + + if (gltf_node->skeleton >= 0) { + Transform xform; + xform.basis.set_quat_scale(rot, scale); + xform.origin = pos; + + const Skeleton3D *skeleton = state->skeletons[gltf_node->skeleton]->godot_skeleton; + const int bone_idx = skeleton->find_bone(gltf_node->get_name()); + xform = skeleton->get_bone_rest(bone_idx).affine_inverse() * xform; + + rot = xform.basis.get_rotation_quat(); + rot.normalize(); + scale = xform.basis.get_scale(); + pos = xform.origin; + } + + animation->transform_track_insert_key(track_idx, time, pos, rot, scale); + + if (last) { + break; + } + time += increment; + if (time >= length) { + last = true; + time = length; + } + } + } + + for (int i = 0; i < track.weight_tracks.size(); i++) { + ERR_CONTINUE(gltf_node->mesh < 0 || gltf_node->mesh >= state->meshes.size()); + Ref<GLTFMesh> mesh = state->meshes[gltf_node->mesh]; + ERR_CONTINUE(mesh.is_null()); + ERR_CONTINUE(mesh->get_mesh().is_null()); + ERR_CONTINUE(mesh->get_mesh()->get_mesh().is_null()); + const String prop = "blend_shapes/" + mesh->get_mesh()->get_blend_shape_name(i); + + const String blend_path = String(node_path) + ":" + prop; + + const int track_idx = animation->get_track_count(); + animation->add_track(Animation::TYPE_VALUE); + animation->track_set_path(track_idx, blend_path); + + // Only LINEAR and STEP (NEAREST) can be supported out of the box by Godot's Animation, + // the other modes have to be baked. + GLTFAnimation::Interpolation gltf_interp = track.weight_tracks[i].interpolation; + if (gltf_interp == GLTFAnimation::INTERP_LINEAR || gltf_interp == GLTFAnimation::INTERP_STEP) { + animation->track_set_interpolation_type(track_idx, gltf_interp == GLTFAnimation::INTERP_STEP ? Animation::INTERPOLATION_NEAREST : Animation::INTERPOLATION_LINEAR); + for (int j = 0; j < track.weight_tracks[i].times.size(); j++) { + const float t = track.weight_tracks[i].times[j]; + const float attribs = track.weight_tracks[i].values[j]; + animation->track_insert_key(track_idx, t, attribs); + } + } else { + // CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies. + const float increment = 1.0 / float(bake_fps); + float time = 0.0; + bool last = false; + while (true) { + _interpolate_track<float>(track.weight_tracks[i].times, track.weight_tracks[i].values, time, gltf_interp); + if (last) { + break; + } + time += increment; + if (time >= length) { + last = true; + time = length; + } + } + } + } + } + + animation->set_length(length); + + ap->add_animation(name, animation); +} + +void GLTFDocument::_convert_mesh_instances(Ref<GLTFState> state) { + for (GLTFNodeIndex mi_node_i = 0; mi_node_i < state->nodes.size(); ++mi_node_i) { + Ref<GLTFNode> node = state->nodes[mi_node_i]; + + if (node->mesh < 0) { + continue; + } + Array json_skins; + if (state->json.has("skins")) { + json_skins = state->json["skins"]; + } + Map<GLTFNodeIndex, Node *>::Element *mi_element = state->scene_nodes.find(mi_node_i); + if (!mi_element) { + continue; + } + MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(mi_element->get()); + ERR_CONTINUE(!mi); + Transform mi_xform = mi->get_transform(); + node->scale = mi_xform.basis.get_scale(); + node->rotation = mi_xform.basis.get_rotation_quat(); + node->translation = mi_xform.origin; + + Dictionary json_skin; + Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(mi->get_node(mi->get_skeleton_path())); + if (!skeleton) { + continue; + } + if (!skeleton->get_bone_count()) { + continue; + } + Ref<Skin> skin = mi->get_skin(); + if (skin.is_null()) { + skin = skeleton->register_skin(nullptr)->get_skin(); + } + Ref<GLTFSkin> gltf_skin; + gltf_skin.instance(); + Array json_joints; + GLTFSkeletonIndex skeleton_gltf_i = -1; + + NodePath skeleton_path = mi->get_skeleton_path(); + bool is_unique = true; + for (int32_t skin_i = 0; skin_i < state->skins.size(); skin_i++) { + Ref<GLTFSkin> prev_gltf_skin = state->skins.write[skin_i]; + if (gltf_skin.is_null()) { + continue; + } + GLTFSkeletonIndex prev_skeleton = prev_gltf_skin->get_skeleton(); + if (prev_skeleton == -1 || prev_skeleton >= state->skeletons.size()) { + continue; + } + if (prev_gltf_skin->get_godot_skin() == skin && state->skeletons[prev_skeleton]->godot_skeleton == skeleton) { + node->skin = skin_i; + node->skeleton = prev_skeleton; + is_unique = false; + break; + } + } + if (!is_unique) { + continue; + } + GLTFSkeletonIndex skeleton_i = _convert_skeleton(state, skeleton); + skeleton_gltf_i = skeleton_i; + ERR_CONTINUE(skeleton_gltf_i == -1); + gltf_skin->skeleton = skeleton_gltf_i; + Ref<GLTFSkeleton> gltf_skeleton = state->skeletons.write[skeleton_gltf_i]; + for (int32_t bind_i = 0; bind_i < skin->get_bind_count(); bind_i++) { + String godot_bone_name = skin->get_bind_name(bind_i); + if (godot_bone_name.is_empty()) { + int32_t bone = skin->get_bind_bone(bind_i); + godot_bone_name = skeleton->get_bone_name(bone); + } + if (skeleton->find_bone(godot_bone_name) == -1) { + godot_bone_name = skeleton->get_bone_name(0); + } + BoneId bone_index = skeleton->find_bone(godot_bone_name); + ERR_CONTINUE(bone_index == -1); + Ref<GLTFNode> joint_node; + joint_node.instance(); + String gltf_bone_name = _gen_unique_bone_name(state, skeleton_gltf_i, godot_bone_name); + joint_node->set_name(gltf_bone_name); + + Transform bone_rest_xform = skeleton->get_bone_rest(bone_index); + joint_node->scale = bone_rest_xform.basis.get_scale(); + joint_node->rotation = bone_rest_xform.basis.get_rotation_quat(); + joint_node->translation = bone_rest_xform.origin; + joint_node->joint = true; + + int32_t joint_node_i = state->nodes.size(); + state->nodes.push_back(joint_node); + gltf_skeleton->godot_bone_node.insert(bone_index, joint_node_i); + int32_t joint_index = gltf_skin->joints.size(); + gltf_skin->joint_i_to_bone_i.insert(joint_index, bone_index); + gltf_skin->joints.push_back(joint_node_i); + gltf_skin->joints_original.push_back(joint_node_i); + gltf_skin->inverse_binds.push_back(skin->get_bind_pose(bind_i)); + json_joints.push_back(joint_node_i); + for (Map<GLTFNodeIndex, Node *>::Element *skin_scene_node_i = state->scene_nodes.front(); skin_scene_node_i; skin_scene_node_i = skin_scene_node_i->next()) { + if (skin_scene_node_i->get() == skeleton) { + gltf_skin->skin_root = skin_scene_node_i->key(); + json_skin["skeleton"] = skin_scene_node_i->key(); + } + } + gltf_skin->godot_skin = skin; + gltf_skin->set_name(_gen_unique_name(state, skin->get_name())); + } + for (int32_t bind_i = 0; bind_i < skin->get_bind_count(); bind_i++) { + String bone_name = skeleton->get_bone_name(bind_i); + String godot_bone_name = skin->get_bind_name(bind_i); + int32_t bone = -1; + if (skin->get_bind_bone(bind_i) != -1) { + bone = skin->get_bind_bone(bind_i); + godot_bone_name = skeleton->get_bone_name(bone); + } + bone = skeleton->find_bone(godot_bone_name); + if (bone == -1) { + continue; + } + BoneId bone_parent = skeleton->get_bone_parent(bone); + GLTFNodeIndex joint_node_i = gltf_skeleton->godot_bone_node[bone]; + ERR_CONTINUE(joint_node_i >= state->nodes.size()); + if (bone_parent != -1) { + GLTFNodeIndex parent_joint_gltf_node = gltf_skin->joints[bone_parent]; + Ref<GLTFNode> parent_joint_node = state->nodes.write[parent_joint_gltf_node]; + parent_joint_node->children.push_back(joint_node_i); + } else { + Node *node_parent = skeleton->get_parent(); + ERR_CONTINUE(!node_parent); + for (Map<GLTFNodeIndex, Node *>::Element *E = state->scene_nodes.front(); E; E = E->next()) { + if (E->get() == node_parent) { + GLTFNodeIndex gltf_node_i = E->key(); + Ref<GLTFNode> gltf_node = state->nodes.write[gltf_node_i]; + gltf_node->children.push_back(joint_node_i); + break; + } + } + } + } + _expand_skin(state, gltf_skin); + node->skin = state->skins.size(); + state->skins.push_back(gltf_skin); + + json_skin["inverseBindMatrices"] = _encode_accessor_as_xform(state, gltf_skin->inverse_binds, false); + json_skin["joints"] = json_joints; + json_skin["name"] = gltf_skin->get_name(); + json_skins.push_back(json_skin); + state->json["skins"] = json_skins; + } +} + +float GLTFDocument::solve_metallic(float p_dielectric_specular, float diffuse, float specular, float p_one_minus_specular_strength) { + if (specular <= p_dielectric_specular) { + return 0.0f; + } + + const float a = p_dielectric_specular; + const float b = diffuse * p_one_minus_specular_strength / (1.0f - p_dielectric_specular) + specular - 2.0f * p_dielectric_specular; + const float c = p_dielectric_specular - specular; + const float D = b * b - 4.0f * a * c; + return CLAMP((-b + Math::sqrt(D)) / (2.0f * a), 0.0f, 1.0f); +} + +float GLTFDocument::get_perceived_brightness(const Color p_color) { + const Color coeff = Color(R_BRIGHTNESS_COEFF, G_BRIGHTNESS_COEFF, B_BRIGHTNESS_COEFF); + const Color value = coeff * (p_color * p_color); + + const float r = value.r; + const float g = value.g; + const float b = value.b; + + return Math::sqrt(r + g + b); +} + +float GLTFDocument::get_max_component(const Color &p_color) { + const float r = p_color.r; + const float g = p_color.g; + const float b = p_color.b; + + return MAX(MAX(r, g), b); +} + +void GLTFDocument::_process_mesh_instances(Ref<GLTFState> state, Node *scene_root) { + for (GLTFNodeIndex node_i = 0; node_i < state->nodes.size(); ++node_i) { + Ref<GLTFNode> node = state->nodes[node_i]; + + if (node->skin >= 0 && node->mesh >= 0) { + const GLTFSkinIndex skin_i = node->skin; + + Map<GLTFNodeIndex, Node *>::Element *mi_element = state->scene_nodes.find(node_i); + EditorSceneImporterMeshNode3D *mi = Object::cast_to<EditorSceneImporterMeshNode3D>(mi_element->get()); + ERR_FAIL_COND(mi == nullptr); + + const GLTFSkeletonIndex skel_i = state->skins.write[node->skin]->skeleton; + Ref<GLTFSkeleton> gltf_skeleton = state->skeletons.write[skel_i]; + Skeleton3D *skeleton = gltf_skeleton->godot_skeleton; + ERR_FAIL_COND(skeleton == nullptr); + + mi->get_parent()->remove_child(mi); + skeleton->add_child(mi); + mi->set_owner(skeleton->get_owner()); + + mi->set_skin(state->skins.write[skin_i]->godot_skin); + mi->set_skeleton_path(mi->get_path_to(skeleton)); + mi->set_transform(Transform()); + } + } +} + +GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state, GLTFAnimation::Track p_track, Ref<Animation> p_animation, Transform p_bone_rest, int32_t p_track_i, GLTFNodeIndex p_node_i) { + Animation::InterpolationType interpolation = p_animation->track_get_interpolation_type(p_track_i); + + GLTFAnimation::Interpolation gltf_interpolation = GLTFAnimation::INTERP_LINEAR; + if (interpolation == Animation::InterpolationType::INTERPOLATION_LINEAR) { + gltf_interpolation = GLTFAnimation::INTERP_LINEAR; + } else if (interpolation == Animation::InterpolationType::INTERPOLATION_NEAREST) { + gltf_interpolation = GLTFAnimation::INTERP_STEP; + } else if (interpolation == Animation::InterpolationType::INTERPOLATION_CUBIC) { + gltf_interpolation = GLTFAnimation::INTERP_CUBIC_SPLINE; + } + Animation::TrackType track_type = p_animation->track_get_type(p_track_i); + int32_t key_count = p_animation->track_get_key_count(p_track_i); + Vector<float> times; + times.resize(key_count); + String path = p_animation->track_get_path(p_track_i); + for (int32_t key_i = 0; key_i < key_count; key_i++) { + times.write[key_i] = p_animation->track_get_key_time(p_track_i, key_i); + } + const float BAKE_FPS = 30.0f; + if (track_type == Animation::TYPE_TRANSFORM) { + p_track.translation_track.times = times; + p_track.translation_track.interpolation = gltf_interpolation; + p_track.rotation_track.times = times; + p_track.rotation_track.interpolation = gltf_interpolation; + p_track.scale_track.times = times; + p_track.scale_track.interpolation = gltf_interpolation; + + p_track.scale_track.values.resize(key_count); + p_track.scale_track.interpolation = gltf_interpolation; + p_track.translation_track.values.resize(key_count); + p_track.translation_track.interpolation = gltf_interpolation; + p_track.rotation_track.values.resize(key_count); + p_track.rotation_track.interpolation = gltf_interpolation; + for (int32_t key_i = 0; key_i < key_count; key_i++) { + Vector3 translation; + Quat rotation; + Vector3 scale; + Error err = p_animation->transform_track_get_key(p_track_i, key_i, &translation, &rotation, &scale); + ERR_CONTINUE(err != OK); + Transform xform; + xform.basis.set_quat_scale(rotation, scale); + xform.origin = translation; + xform = p_bone_rest * xform; + p_track.translation_track.values.write[key_i] = xform.get_origin(); + p_track.rotation_track.values.write[key_i] = xform.basis.get_rotation_quat(); + p_track.scale_track.values.write[key_i] = xform.basis.get_scale(); + } + } else if (path.find(":transform") != -1) { + p_track.translation_track.times = times; + p_track.translation_track.interpolation = gltf_interpolation; + p_track.rotation_track.times = times; + p_track.rotation_track.interpolation = gltf_interpolation; + p_track.scale_track.times = times; + p_track.scale_track.interpolation = gltf_interpolation; + + p_track.scale_track.values.resize(key_count); + p_track.scale_track.interpolation = gltf_interpolation; + p_track.translation_track.values.resize(key_count); + p_track.translation_track.interpolation = gltf_interpolation; + p_track.rotation_track.values.resize(key_count); + p_track.rotation_track.interpolation = gltf_interpolation; + for (int32_t key_i = 0; key_i < key_count; key_i++) { + Transform xform = p_animation->track_get_key_value(p_track_i, key_i); + p_track.translation_track.values.write[key_i] = xform.get_origin(); + p_track.rotation_track.values.write[key_i] = xform.basis.get_rotation_quat(); + p_track.scale_track.values.write[key_i] = xform.basis.get_scale(); + } + } else if (track_type == Animation::TYPE_VALUE) { + if (path.find("/rotation_quat") != -1) { + p_track.rotation_track.times = times; + p_track.rotation_track.interpolation = gltf_interpolation; + + p_track.rotation_track.values.resize(key_count); + p_track.rotation_track.interpolation = gltf_interpolation; + + for (int32_t key_i = 0; key_i < key_count; key_i++) { + Quat rotation_track = p_animation->track_get_key_value(p_track_i, key_i); + p_track.rotation_track.values.write[key_i] = rotation_track; + } + } else if (path.find(":translation") != -1) { + p_track.translation_track.times = times; + p_track.translation_track.interpolation = gltf_interpolation; + + p_track.translation_track.values.resize(key_count); + p_track.translation_track.interpolation = gltf_interpolation; + + for (int32_t key_i = 0; key_i < key_count; key_i++) { + Vector3 translation = p_animation->track_get_key_value(p_track_i, key_i); + p_track.translation_track.values.write[key_i] = translation; + } + } else if (path.find(":rotation_degrees") != -1) { + p_track.rotation_track.times = times; + p_track.rotation_track.interpolation = gltf_interpolation; + + p_track.rotation_track.values.resize(key_count); + p_track.rotation_track.interpolation = gltf_interpolation; + + for (int32_t key_i = 0; key_i < key_count; key_i++) { + Quat rotation; + Vector3 rotation_degrees = p_animation->track_get_key_value(p_track_i, key_i); + Vector3 rotation_radian; + rotation_radian.x = Math::deg2rad(rotation_degrees.x); + rotation_radian.y = Math::deg2rad(rotation_degrees.y); + rotation_radian.z = Math::deg2rad(rotation_degrees.z); + rotation.set_euler(rotation_radian); + p_track.rotation_track.values.write[key_i] = rotation; + } + } else if (path.find(":scale") != -1) { + p_track.scale_track.times = times; + p_track.scale_track.interpolation = gltf_interpolation; + + p_track.scale_track.values.resize(key_count); + p_track.scale_track.interpolation = gltf_interpolation; + + for (int32_t key_i = 0; key_i < key_count; key_i++) { + Vector3 scale_track = p_animation->track_get_key_value(p_track_i, key_i); + p_track.scale_track.values.write[key_i] = scale_track; + } + } + } else if (track_type == Animation::TYPE_BEZIER) { + if (path.find("/scale") != -1) { + const int32_t keys = p_animation->track_get_key_time(p_track_i, key_count - 1) * BAKE_FPS; + if (!p_track.scale_track.times.size()) { + Vector<float> new_times; + new_times.resize(keys); + for (int32_t key_i = 0; key_i < keys; key_i++) { + new_times.write[key_i] = key_i / BAKE_FPS; + } + p_track.scale_track.times = new_times; + p_track.scale_track.interpolation = gltf_interpolation; + + p_track.scale_track.values.resize(keys); + + for (int32_t key_i = 0; key_i < keys; key_i++) { + p_track.scale_track.values.write[key_i] = Vector3(1.0f, 1.0f, 1.0f); + } + p_track.scale_track.interpolation = gltf_interpolation; + } + + for (int32_t key_i = 0; key_i < keys; key_i++) { + Vector3 bezier_track = p_track.scale_track.values[key_i]; + if (path.find("/scale:x") != -1) { + bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS); + bezier_track.x = p_bone_rest.affine_inverse().basis.get_scale().x * bezier_track.x; + } else if (path.find("/scale:y") != -1) { + bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS); + bezier_track.y = p_bone_rest.affine_inverse().basis.get_scale().y * bezier_track.y; + } else if (path.find("/scale:z") != -1) { + bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS); + bezier_track.z = p_bone_rest.affine_inverse().basis.get_scale().z * bezier_track.z; + } + p_track.scale_track.values.write[key_i] = bezier_track; + } + } else if (path.find("/translation") != -1) { + const int32_t keys = p_animation->track_get_key_time(p_track_i, key_count - 1) * BAKE_FPS; + if (!p_track.translation_track.times.size()) { + Vector<float> new_times; + new_times.resize(keys); + for (int32_t key_i = 0; key_i < keys; key_i++) { + new_times.write[key_i] = key_i / BAKE_FPS; + } + p_track.translation_track.times = new_times; + p_track.translation_track.interpolation = gltf_interpolation; + + p_track.translation_track.values.resize(keys); + p_track.translation_track.interpolation = gltf_interpolation; + } + + for (int32_t key_i = 0; key_i < keys; key_i++) { + Vector3 bezier_track = p_track.translation_track.values[key_i]; + if (path.find("/translation:x") != -1) { + bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS); + bezier_track.x = p_bone_rest.affine_inverse().origin.x * bezier_track.x; + } else if (path.find("/translation:y") != -1) { + bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS); + bezier_track.y = p_bone_rest.affine_inverse().origin.y * bezier_track.y; + } else if (path.find("/translation:z") != -1) { + bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS); + bezier_track.z = p_bone_rest.affine_inverse().origin.z * bezier_track.z; + } + p_track.translation_track.values.write[key_i] = bezier_track; + } + } + } + + return p_track; +} + +void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap, String p_animation_track_name) { + Ref<Animation> animation = ap->get_animation(p_animation_track_name); + Ref<GLTFAnimation> gltf_animation; + gltf_animation.instance(); + gltf_animation->set_name(_gen_unique_name(state, p_animation_track_name)); + + for (int32_t track_i = 0; track_i < animation->get_track_count(); track_i++) { + if (!animation->track_is_enabled(track_i)) { + continue; + } + String orig_track_path = animation->track_get_path(track_i); + if (String(orig_track_path).find(":translation") != -1) { + const Vector<String> node_suffix = String(orig_track_path).split(":translation"); + const NodePath path = node_suffix[0]; + const Node *node = ap->get_parent()->get_node_or_null(path); + for (Map<GLTFNodeIndex, Node *>::Element *translation_scene_node_i = state->scene_nodes.front(); translation_scene_node_i; translation_scene_node_i = translation_scene_node_i->next()) { + if (translation_scene_node_i->get() == node) { + GLTFNodeIndex node_index = translation_scene_node_i->key(); + Map<int, GLTFAnimation::Track>::Element *translation_track_i = gltf_animation->get_tracks().find(node_index); + GLTFAnimation::Track track; + if (translation_track_i) { + track = translation_track_i->get(); + } + track = _convert_animation_track(state, track, animation, Transform(), track_i, node_index); + gltf_animation->get_tracks().insert(node_index, track); + } + } + } else if (String(orig_track_path).find(":rotation_degrees") != -1) { + const Vector<String> node_suffix = String(orig_track_path).split(":rotation_degrees"); + const NodePath path = node_suffix[0]; + const Node *node = ap->get_parent()->get_node_or_null(path); + for (Map<GLTFNodeIndex, Node *>::Element *rotation_degree_scene_node_i = state->scene_nodes.front(); rotation_degree_scene_node_i; rotation_degree_scene_node_i = rotation_degree_scene_node_i->next()) { + if (rotation_degree_scene_node_i->get() == node) { + GLTFNodeIndex node_index = rotation_degree_scene_node_i->key(); + Map<int, GLTFAnimation::Track>::Element *rotation_degree_track_i = gltf_animation->get_tracks().find(node_index); + GLTFAnimation::Track track; + if (rotation_degree_track_i) { + track = rotation_degree_track_i->get(); + } + track = _convert_animation_track(state, track, animation, Transform(), track_i, node_index); + gltf_animation->get_tracks().insert(node_index, track); + } + } + } else if (String(orig_track_path).find(":scale") != -1) { + const Vector<String> node_suffix = String(orig_track_path).split(":scale"); + const NodePath path = node_suffix[0]; + const Node *node = ap->get_parent()->get_node_or_null(path); + for (Map<GLTFNodeIndex, Node *>::Element *scale_scene_node_i = state->scene_nodes.front(); scale_scene_node_i; scale_scene_node_i = scale_scene_node_i->next()) { + if (scale_scene_node_i->get() == node) { + GLTFNodeIndex node_index = scale_scene_node_i->key(); + Map<int, GLTFAnimation::Track>::Element *scale_track_i = gltf_animation->get_tracks().find(node_index); + GLTFAnimation::Track track; + if (scale_track_i) { + track = scale_track_i->get(); + } + track = _convert_animation_track(state, track, animation, Transform(), track_i, node_index); + gltf_animation->get_tracks().insert(node_index, track); + } + } + } else if (String(orig_track_path).find(":transform") != -1) { + const Vector<String> node_suffix = String(orig_track_path).split(":transform"); + const NodePath path = node_suffix[0]; + const Node *node = ap->get_parent()->get_node_or_null(path); + for (Map<GLTFNodeIndex, Node *>::Element *transform_track_i = state->scene_nodes.front(); transform_track_i; transform_track_i = transform_track_i->next()) { + if (transform_track_i->get() == node) { + GLTFAnimation::Track track; + track = _convert_animation_track(state, track, animation, Transform(), track_i, transform_track_i->key()); + gltf_animation->get_tracks().insert(transform_track_i->key(), track); + } + } + } else if (String(orig_track_path).find(":blend_shapes/") != -1) { + const Vector<String> node_suffix = String(orig_track_path).split(":blend_shapes/"); + const NodePath path = node_suffix[0]; + const String suffix = node_suffix[1]; + const Node *node = ap->get_parent()->get_node_or_null(path); + for (Map<GLTFNodeIndex, Node *>::Element *transform_track_i = state->scene_nodes.front(); transform_track_i; transform_track_i = transform_track_i->next()) { + if (transform_track_i->get() == node) { + const MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(node); + if (!mi) { + continue; + } + Ref<ArrayMesh> array_mesh = mi->get_mesh(); + if (array_mesh.is_null()) { + continue; + } + if (node_suffix.size() != 2) { + continue; + } + GLTFNodeIndex mesh_index = -1; + for (GLTFNodeIndex node_i = 0; node_i < state->scene_nodes.size(); node_i++) { + if (state->scene_nodes[node_i] == node) { + mesh_index = node_i; + break; + } + } + ERR_CONTINUE(mesh_index == -1); + Ref<Mesh> mesh = mi->get_mesh(); + ERR_CONTINUE(mesh.is_null()); + for (int32_t shape_i = 0; shape_i < mesh->get_blend_shape_count(); shape_i++) { + if (mesh->get_blend_shape_name(shape_i) != suffix) { + continue; + } + GLTFAnimation::Track track; + Map<int, GLTFAnimation::Track>::Element *blend_shape_track_i = gltf_animation->get_tracks().find(mesh_index); + if (blend_shape_track_i) { + track = blend_shape_track_i->get(); + } + Animation::InterpolationType interpolation = animation->track_get_interpolation_type(track_i); + + GLTFAnimation::Interpolation gltf_interpolation = GLTFAnimation::INTERP_LINEAR; + if (interpolation == Animation::InterpolationType::INTERPOLATION_LINEAR) { + gltf_interpolation = GLTFAnimation::INTERP_LINEAR; + } else if (interpolation == Animation::InterpolationType::INTERPOLATION_NEAREST) { + gltf_interpolation = GLTFAnimation::INTERP_STEP; + } else if (interpolation == Animation::InterpolationType::INTERPOLATION_CUBIC) { + gltf_interpolation = GLTFAnimation::INTERP_CUBIC_SPLINE; + } + Animation::TrackType track_type = animation->track_get_type(track_i); + if (track_type == Animation::TYPE_VALUE) { + int32_t key_count = animation->track_get_key_count(track_i); + GLTFAnimation::Channel<float> weight; + weight.interpolation = gltf_interpolation; + weight.times.resize(key_count); + for (int32_t time_i = 0; time_i < key_count; time_i++) { + weight.times.write[time_i] = animation->track_get_key_time(track_i, time_i); + } + weight.values.resize(key_count); + for (int32_t value_i = 0; value_i < key_count; value_i++) { + weight.values.write[value_i] = animation->track_get_key_value(track_i, value_i); + } + track.weight_tracks.push_back(weight); + } + gltf_animation->get_tracks()[mesh_index] = track; + } + } + } + + } else if (String(orig_track_path).find(":") != -1) { + //Process skeleton + const Vector<String> node_suffix = String(orig_track_path).split(":"); + const String node = node_suffix[0]; + const NodePath node_path = node; + const String suffix = node_suffix[1]; + Node *godot_node = ap->get_parent()->get_node_or_null(node_path); + Skeleton3D *skeleton = nullptr; + GLTFSkeletonIndex skeleton_gltf_i = -1; + for (GLTFSkeletonIndex skeleton_i = 0; skeleton_i < state->skeletons.size(); skeleton_i++) { + if (state->skeletons[skeleton_i]->godot_skeleton == cast_to<Skeleton3D>(godot_node)) { + skeleton = state->skeletons[skeleton_i]->godot_skeleton; + skeleton_gltf_i = skeleton_i; + ERR_CONTINUE(!skeleton); + Ref<GLTFSkeleton> skeleton_gltf = state->skeletons[skeleton_gltf_i]; + int32_t bone = skeleton->find_bone(suffix); + ERR_CONTINUE(bone == -1); + Transform xform = skeleton->get_bone_rest(bone); + if (!skeleton_gltf->godot_bone_node.has(bone)) { + continue; + } + GLTFNodeIndex node_i = skeleton_gltf->godot_bone_node[bone]; + Map<int, GLTFAnimation::Track>::Element *property_track_i = gltf_animation->get_tracks().find(node_i); + GLTFAnimation::Track track; + if (property_track_i) { + track = property_track_i->get(); + } + track = _convert_animation_track(state, track, animation, xform, track_i, node_i); + gltf_animation->get_tracks()[node_i] = track; + } + } + } else if (String(orig_track_path).find(":") == -1) { + const Node *node = ap->get_parent()->get_node_or_null(orig_track_path); + for (Map<GLTFNodeIndex, Node *>::Element *scene_node_i = state->scene_nodes.front(); scene_node_i; scene_node_i = scene_node_i->next()) { + if (scene_node_i->get() == node) { + GLTFNodeIndex node_index = scene_node_i->key(); + Map<int, GLTFAnimation::Track>::Element *node_track_i = gltf_animation->get_tracks().find(node_index); + GLTFAnimation::Track track; + if (node_track_i) { + track = node_track_i->get(); + } + track = _convert_animation_track(state, track, animation, Transform(), track_i, node_index); + gltf_animation->get_tracks().insert(node_index, track); + break; + } + } + } + } + if (gltf_animation->get_tracks().size()) { + state->animations.push_back(gltf_animation); + } +} + +Error GLTFDocument::parse(Ref<GLTFState> state, String p_path, bool p_read_binary) { + Error err; + FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err); + if (!f) { + return err; + } + uint32_t magic = f->get_32(); + if (magic == 0x46546C67) { + //binary file + //text file + err = _parse_glb(p_path, state); + if (err) + return FAILED; + } else { + //text file + err = _parse_json(p_path, state); + if (err) + return FAILED; + } + f->close(); + + ERR_FAIL_COND_V(!state->json.has("asset"), Error::FAILED); + + Dictionary asset = state->json["asset"]; + + ERR_FAIL_COND_V(!asset.has("version"), Error::FAILED); + + String version = asset["version"]; + + state->major_version = version.get_slice(".", 0).to_int(); + state->minor_version = version.get_slice(".", 1).to_int(); + + /* STEP 0 PARSE SCENE */ + err = _parse_scenes(state); + if (err != OK) + return Error::FAILED; + + /* STEP 1 PARSE NODES */ + err = _parse_nodes(state); + if (err != OK) + return Error::FAILED; + + /* STEP 2 PARSE BUFFERS */ + err = _parse_buffers(state, p_path.get_base_dir()); + if (err != OK) + return Error::FAILED; + + /* STEP 3 PARSE BUFFER VIEWS */ + err = _parse_buffer_views(state); + if (err != OK) + return Error::FAILED; + + /* STEP 4 PARSE ACCESSORS */ + err = _parse_accessors(state); + if (err != OK) + return Error::FAILED; + + /* STEP 5 PARSE IMAGES */ + err = _parse_images(state, p_path.get_base_dir()); + if (err != OK) + return Error::FAILED; + + /* STEP 6 PARSE TEXTURES */ + err = _parse_textures(state); + if (err != OK) + return Error::FAILED; + + /* STEP 7 PARSE TEXTURES */ + err = _parse_materials(state); + if (err != OK) + return Error::FAILED; + + /* STEP 9 PARSE SKINS */ + err = _parse_skins(state); + if (err != OK) + return Error::FAILED; + + /* STEP 10 DETERMINE SKELETONS */ + err = _determine_skeletons(state); + if (err != OK) + return Error::FAILED; + + /* STEP 11 CREATE SKELETONS */ + err = _create_skeletons(state); + if (err != OK) + return Error::FAILED; + + /* STEP 12 CREATE SKINS */ + err = _create_skins(state); + if (err != OK) + return Error::FAILED; + + /* STEP 13 PARSE MESHES (we have enough info now) */ + err = _parse_meshes(state); + if (err != OK) + return Error::FAILED; + + /* STEP 14 PARSE LIGHTS */ + err = _parse_lights(state); + if (err != OK) { + return Error::FAILED; + } + + /* STEP 15 PARSE CAMERAS */ + err = _parse_cameras(state); + if (err != OK) + return Error::FAILED; + + /* STEP 16 PARSE ANIMATIONS */ + err = _parse_animations(state); + if (err != OK) + return Error::FAILED; + + /* STEP 17 ASSIGN SCENE NAMES */ + _assign_scene_names(state); + + return OK; +} + +Dictionary GLTFDocument::_serialize_texture_transform_uv2(Ref<BaseMaterial3D> p_material) { + Dictionary extension; + Ref<BaseMaterial3D> mat = p_material; + if (mat.is_valid()) { + Dictionary texture_transform; + Array offset; + offset.resize(2); + offset[0] = mat->get_uv2_offset().x; + offset[1] = mat->get_uv2_offset().y; + texture_transform["offset"] = offset; + Array scale; + scale.resize(2); + scale[0] = mat->get_uv2_scale().x; + scale[1] = mat->get_uv2_scale().y; + texture_transform["scale"] = scale; + // Godot doesn't support texture rotation + extension["KHR_texture_transform"] = texture_transform; + } + return extension; +} + +Dictionary GLTFDocument::_serialize_texture_transform_uv1(Ref<BaseMaterial3D> p_material) { + Dictionary extension; + if (p_material.is_valid()) { + Dictionary texture_transform; + Array offset; + offset.resize(2); + offset[0] = p_material->get_uv1_offset().x; + offset[1] = p_material->get_uv1_offset().y; + texture_transform["offset"] = offset; + Array scale; + scale.resize(2); + scale[0] = p_material->get_uv1_scale().x; + scale[1] = p_material->get_uv1_scale().y; + texture_transform["scale"] = scale; + // Godot doesn't support texture rotation + extension["KHR_texture_transform"] = texture_transform; + } + return extension; +} + +Error GLTFDocument::_serialize_version(Ref<GLTFState> state) { + const String version = "2.0"; + state->major_version = version.get_slice(".", 0).to_int(); + state->minor_version = version.get_slice(".", 1).to_int(); + Dictionary asset; + asset["version"] = version; + + String hash = VERSION_HASH; + asset["generator"] = String(VERSION_FULL_NAME) + String("@") + (hash.length() == 0 ? String("unknown") : hash); + state->json["asset"] = asset; + ERR_FAIL_COND_V(!asset.has("version"), Error::FAILED); + ERR_FAIL_COND_V(!state->json.has("asset"), Error::FAILED); + return OK; +} + +Error GLTFDocument::_serialize_file(Ref<GLTFState> state, const String p_path) { + Error err = FAILED; + if (p_path.to_lower().ends_with("glb")) { + err = _encode_buffer_glb(state, p_path); + ERR_FAIL_COND_V(err != OK, err); + FileAccessRef f = FileAccess::open(p_path, FileAccess::WRITE, &err); + ERR_FAIL_COND_V(!f, FAILED); + + String json = JSON::print(state->json); + + const uint32_t magic = 0x46546C67; // GLTF + const int32_t header_size = 12; + const int32_t chunk_header_size = 8; + + for (int32_t pad_i = 0; pad_i < (chunk_header_size + json.utf8().length()) % 4; pad_i++) { + json += " "; + } + CharString cs = json.utf8(); + const uint32_t text_chunk_length = cs.length(); + + const uint32_t text_chunk_type = 0x4E4F534A; //JSON + int32_t binary_data_length = 0; + if (state->buffers.size()) { + binary_data_length = state->buffers[0].size(); + } + const int32_t binary_chunk_length = binary_data_length; + const int32_t binary_chunk_type = 0x004E4942; //BIN + + f->create(FileAccess::ACCESS_RESOURCES); + f->store_32(magic); + f->store_32(state->major_version); // version + f->store_32(header_size + chunk_header_size + text_chunk_length + chunk_header_size + binary_data_length); // length + f->store_32(text_chunk_length); + f->store_32(text_chunk_type); + f->store_buffer((uint8_t *)&cs[0], cs.length()); + if (binary_chunk_length) { + f->store_32(binary_chunk_length); + f->store_32(binary_chunk_type); + f->store_buffer(state->buffers[0].ptr(), binary_data_length); + } + + f->close(); + } else { + err = _encode_buffer_bins(state, p_path); + ERR_FAIL_COND_V(err != OK, err); + FileAccessRef f = FileAccess::open(p_path, FileAccess::WRITE, &err); + ERR_FAIL_COND_V(!f, FAILED); + + f->create(FileAccess::ACCESS_RESOURCES); + String json = JSON::print(state->json); + f->store_string(json); + f->close(); + } + return err; +} diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h new file mode 100644 index 0000000000..111324d1e6 --- /dev/null +++ b/modules/gltf/gltf_document.h @@ -0,0 +1,428 @@ +/*************************************************************************/ +/* gltf_document.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef GLTF_DOCUMENT_H +#define GLTF_DOCUMENT_H + +#include "editor/import/resource_importer_scene.h" +#include "editor/import/scene_importer_mesh_node_3d.h" +#include "gltf_animation.h" +#include "scene/2d/node_2d.h" +#include "scene/3d/bone_attachment_3d.h" +#include "scene/3d/light_3d.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/material.h" +#include "scene/resources/texture.h" + +class GLTFState; +class GLTFSkin; +class GLTFNode; +class GLTFSpecGloss; +class GLTFSkeleton; + +using GLTFAccessorIndex = int; +using GLTFAnimationIndex = int; +using GLTFBufferIndex = int; +using GLTFBufferViewIndex = int; +using GLTFCameraIndex = int; +using GLTFImageIndex = int; +using GLTFMaterialIndex = int; +using GLTFMeshIndex = int; +using GLTFLightIndex = int; +using GLTFNodeIndex = int; +using GLTFSkeletonIndex = int; +using GLTFSkinIndex = int; +using GLTFTextureIndex = int; + +class GLTFDocument : public Resource { + GDCLASS(GLTFDocument, Resource); + friend class GLTFState; + friend class GLTFSkin; + friend class GLTFSkeleton; + +public: + const int32_t JOINT_GROUP_SIZE = 4; + enum GLTFType { + TYPE_SCALAR, + TYPE_VEC2, + TYPE_VEC3, + TYPE_VEC4, + TYPE_MAT2, + TYPE_MAT3, + TYPE_MAT4, + }; + + enum { + ARRAY_BUFFER = 34962, + ELEMENT_ARRAY_BUFFER = 34963, + + TYPE_BYTE = 5120, + TYPE_UNSIGNED_BYTE = 5121, + TYPE_SHORT = 5122, + TYPE_UNSIGNED_SHORT = 5123, + TYPE_UNSIGNED_INT = 5125, + TYPE_FLOAT = 5126, + + COMPONENT_TYPE_BYTE = 5120, + COMPONENT_TYPE_UNSIGNED_BYTE = 5121, + COMPONENT_TYPE_SHORT = 5122, + COMPONENT_TYPE_UNSIGNED_SHORT = 5123, + COMPONENT_TYPE_INT = 5125, + COMPONENT_TYPE_FLOAT = 5126, + }; + +private: + template <class T> + static Array to_array(const Vector<T> &p_inp) { + Array ret; + for (int i = 0; i < p_inp.size(); i++) { + ret.push_back(p_inp[i]); + } + return ret; + } + + template <class T> + static Array to_array(const Set<T> &p_inp) { + Array ret; + typename Set<T>::Element *elem = p_inp.front(); + while (elem) { + ret.push_back(elem->get()); + elem = elem->next(); + } + return ret; + } + + template <class T> + static void set_from_array(Vector<T> &r_out, const Array &p_inp) { + r_out.clear(); + for (int i = 0; i < p_inp.size(); i++) { + r_out.push_back(p_inp[i]); + } + } + + template <class T> + static void set_from_array(Set<T> &r_out, const Array &p_inp) { + r_out.clear(); + for (int i = 0; i < p_inp.size(); i++) { + r_out.insert(p_inp[i]); + } + } + template <class K, class V> + static Dictionary to_dict(const Map<K, V> &p_inp) { + Dictionary ret; + for (typename Map<K, V>::Element *E = p_inp.front(); E; E = E->next()) { + ret[E->key()] = E->value(); + } + return ret; + } + + template <class K, class V> + static void set_from_dict(Map<K, V> &r_out, const Dictionary &p_inp) { + r_out.clear(); + Array keys = p_inp.keys(); + for (int i = 0; i < keys.size(); i++) { + r_out[keys[i]] = p_inp[keys[i]]; + } + } + double _filter_number(double p_float); + String _get_component_type_name(const uint32_t p_component); + int _get_component_type_size(const int component_type); + Error _parse_scenes(Ref<GLTFState> state); + Error _parse_nodes(Ref<GLTFState> state); + String _get_type_name(const GLTFType p_component); + String _get_accessor_type_name(const GLTFDocument::GLTFType p_type); + String _sanitize_scene_name(const String &name); + String _gen_unique_name(Ref<GLTFState> state, const String &p_name); + String _sanitize_bone_name(const String &name); + String _gen_unique_bone_name(Ref<GLTFState> state, + const GLTFSkeletonIndex skel_i, + const String &p_name); + GLTFTextureIndex _set_texture(Ref<GLTFState> state, Ref<Texture2D> p_texture); + Ref<Texture2D> _get_texture(Ref<GLTFState> state, + const GLTFTextureIndex p_texture); + Error _parse_json(const String &p_path, Ref<GLTFState> state); + Error _parse_glb(const String &p_path, Ref<GLTFState> state); + void _compute_node_heights(Ref<GLTFState> state); + Error _parse_buffers(Ref<GLTFState> state, const String &p_base_path); + Error _parse_buffer_views(Ref<GLTFState> state); + GLTFType _get_type_from_str(const String &p_string); + Error _parse_accessors(Ref<GLTFState> state); + Error _decode_buffer_view(Ref<GLTFState> state, double *dst, + const GLTFBufferViewIndex p_buffer_view, + const int skip_every, const int skip_bytes, + const int element_size, const int count, + const GLTFType type, const int component_count, + const int component_type, const int component_size, + const bool normalized, const int byte_offset, + const bool for_vertex); + Vector<double> _decode_accessor(Ref<GLTFState> state, + const GLTFAccessorIndex p_accessor, + const bool p_for_vertex); + Vector<float> _decode_accessor_as_floats(Ref<GLTFState> state, + const GLTFAccessorIndex p_accessor, + const bool p_for_vertex); + Vector<int> _decode_accessor_as_ints(Ref<GLTFState> state, + const GLTFAccessorIndex p_accessor, + const bool p_for_vertex); + Vector<Vector2> _decode_accessor_as_vec2(Ref<GLTFState> state, + const GLTFAccessorIndex p_accessor, + const bool p_for_vertex); + Vector<Vector3> _decode_accessor_as_vec3(Ref<GLTFState> state, + const GLTFAccessorIndex p_accessor, + const bool p_for_vertex); + Vector<Color> _decode_accessor_as_color(Ref<GLTFState> state, + const GLTFAccessorIndex p_accessor, + const bool p_for_vertex); + Vector<Quat> _decode_accessor_as_quat(Ref<GLTFState> state, + const GLTFAccessorIndex p_accessor, + const bool p_for_vertex); + Vector<Transform2D> _decode_accessor_as_xform2d(Ref<GLTFState> state, + const GLTFAccessorIndex p_accessor, + const bool p_for_vertex); + Vector<Basis> _decode_accessor_as_basis(Ref<GLTFState> state, + const GLTFAccessorIndex p_accessor, + const bool p_for_vertex); + Vector<Transform> _decode_accessor_as_xform(Ref<GLTFState> state, + const GLTFAccessorIndex p_accessor, + const bool p_for_vertex); + Error _parse_meshes(Ref<GLTFState> state); + Error _serialize_textures(Ref<GLTFState> state); + Error _serialize_images(Ref<GLTFState> state, const String &p_path); + Error _serialize_lights(Ref<GLTFState> state); + Error _parse_images(Ref<GLTFState> state, const String &p_base_path); + Error _parse_textures(Ref<GLTFState> state); + Error _parse_materials(Ref<GLTFState> state); + void _set_texture_transform_uv1(const Dictionary &d, Ref<BaseMaterial3D> material); + void spec_gloss_to_rough_metal(Ref<GLTFSpecGloss> r_spec_gloss, + Ref<BaseMaterial3D> p_material); + static void spec_gloss_to_metal_base_color(const Color &p_specular_factor, + const Color &p_diffuse, + Color &r_base_color, + float &r_metallic); + GLTFNodeIndex _find_highest_node(Ref<GLTFState> state, + const Vector<GLTFNodeIndex> &subset); + bool _capture_nodes_in_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin, + const GLTFNodeIndex node_index); + void _capture_nodes_for_multirooted_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin); + Error _expand_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin); + Error _verify_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin); + Error _parse_skins(Ref<GLTFState> state); + Error _determine_skeletons(Ref<GLTFState> state); + Error _reparent_non_joint_skeleton_subtrees( + Ref<GLTFState> state, Ref<GLTFSkeleton> skeleton, + const Vector<GLTFNodeIndex> &non_joints); + Error _reparent_to_fake_joint(Ref<GLTFState> state, Ref<GLTFSkeleton> skeleton, + const GLTFNodeIndex node_index); + Error _determine_skeleton_roots(Ref<GLTFState> state, + const GLTFSkeletonIndex skel_i); + Error _create_skeletons(Ref<GLTFState> state); + Error _map_skin_joints_indices_to_skeleton_bone_indices(Ref<GLTFState> state); + Error _serialize_skins(Ref<GLTFState> state); + Error _create_skins(Ref<GLTFState> state); + bool _skins_are_same(const Ref<Skin> skin_a, const Ref<Skin> skin_b); + void _remove_duplicate_skins(Ref<GLTFState> state); + Error _serialize_cameras(Ref<GLTFState> state); + Error _parse_cameras(Ref<GLTFState> state); + Error _parse_lights(Ref<GLTFState> state); + Error _parse_animations(Ref<GLTFState> state); + Error _serialize_animations(Ref<GLTFState> state); + BoneAttachment3D *_generate_bone_attachment(Ref<GLTFState> state, + Skeleton3D *skeleton, + const GLTFNodeIndex node_index); + EditorSceneImporterMeshNode3D *_generate_mesh_instance(Ref<GLTFState> state, Node *scene_parent, const GLTFNodeIndex node_index); + Camera3D *_generate_camera(Ref<GLTFState> state, Node *scene_parent, + const GLTFNodeIndex node_index); + Light3D *_generate_light(Ref<GLTFState> state, Node *scene_parent, const GLTFNodeIndex node_index); + Node3D *_generate_spatial(Ref<GLTFState> state, Node *scene_parent, + const GLTFNodeIndex node_index); + void _assign_scene_names(Ref<GLTFState> state); + template <class T> + T _interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, + const float p_time, + const GLTFAnimation::Interpolation p_interp); + GLTFAccessorIndex _encode_accessor_as_quats(Ref<GLTFState> state, + const Vector<Quat> p_attribs, + const bool p_for_vertex); + GLTFAccessorIndex _encode_accessor_as_weights(Ref<GLTFState> state, + const Vector<Color> p_attribs, + const bool p_for_vertex); + GLTFAccessorIndex _encode_accessor_as_joints(Ref<GLTFState> state, + const Vector<Color> p_attribs, + const bool p_for_vertex); + GLTFAccessorIndex _encode_accessor_as_floats(Ref<GLTFState> state, + const Vector<real_t> p_attribs, + const bool p_for_vertex); + GLTFAccessorIndex _encode_accessor_as_vec2(Ref<GLTFState> state, + const Vector<Vector2> p_attribs, + const bool p_for_vertex); + + void _calc_accessor_vec2_min_max(int i, const int element_count, Vector<double> &type_max, Vector2 attribs, Vector<double> &type_min) { + if (i == 0) { + for (int32_t type_i = 0; type_i < element_count; type_i++) { + type_max.write[type_i] = attribs[(i * element_count) + type_i]; + type_min.write[type_i] = attribs[(i * element_count) + type_i]; + } + } + for (int32_t type_i = 0; type_i < element_count; type_i++) { + type_max.write[type_i] = MAX(attribs[(i * element_count) + type_i], type_max[type_i]); + type_min.write[type_i] = MIN(attribs[(i * element_count) + type_i], type_min[type_i]); + type_max.write[type_i] = _filter_number(type_max.write[type_i]); + type_min.write[type_i] = _filter_number(type_min.write[type_i]); + } + } + + GLTFAccessorIndex _encode_accessor_as_vec3(Ref<GLTFState> state, + const Vector<Vector3> p_attribs, + const bool p_for_vertex); + GLTFAccessorIndex _encode_accessor_as_color(Ref<GLTFState> state, + const Vector<Color> p_attribs, + const bool p_for_vertex); + + void _calc_accessor_min_max(int p_i, const int p_element_count, Vector<double> &p_type_max, Vector<double> p_attribs, Vector<double> &p_type_min); + + GLTFAccessorIndex _encode_accessor_as_ints(Ref<GLTFState> state, + const Vector<int32_t> p_attribs, + const bool p_for_vertex); + GLTFAccessorIndex _encode_accessor_as_xform(Ref<GLTFState> state, + const Vector<Transform> p_attribs, + const bool p_for_vertex); + Error _encode_buffer_view(Ref<GLTFState> state, const double *src, + const int count, const GLTFType type, + const int component_type, const bool normalized, + const int byte_offset, const bool for_vertex, + GLTFBufferViewIndex &r_accessor); + Error _encode_accessors(Ref<GLTFState> state); + Error _encode_buffer_views(Ref<GLTFState> state); + Error _serialize_materials(Ref<GLTFState> state); + Error _serialize_meshes(Ref<GLTFState> state); + Error _serialize_nodes(Ref<GLTFState> state); + Error _serialize_scenes(Ref<GLTFState> state); + String interpolation_to_string(const GLTFAnimation::Interpolation p_interp); + GLTFAnimation::Track _convert_animation_track(Ref<GLTFState> state, + GLTFAnimation::Track p_track, + Ref<Animation> p_animation, Transform p_bone_rest, + int32_t p_track_i, + GLTFNodeIndex p_node_i); + Error _encode_buffer_bins(Ref<GLTFState> state, const String &p_path); + Error _encode_buffer_glb(Ref<GLTFState> state, const String &p_path); + Error _serialize_bone_attachment(Ref<GLTFState> state); + Dictionary _serialize_texture_transform_uv1(Ref<BaseMaterial3D> p_material); + Dictionary _serialize_texture_transform_uv2(Ref<BaseMaterial3D> p_material); + Error _serialize_version(Ref<GLTFState> state); + Error _serialize_file(Ref<GLTFState> state, const String p_path); + Error _serialize_extensions(Ref<GLTFState> state) const; + +public: + // http://www.itu.int/rec/R-REC-BT.601 + // http://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.601-7-201103-I!!PDF-E.pdf + static constexpr float R_BRIGHTNESS_COEFF = 0.299f; + static constexpr float G_BRIGHTNESS_COEFF = 0.587f; + static constexpr float B_BRIGHTNESS_COEFF = 0.114f; + +private: + // https://github.com/microsoft/glTF-SDK/blob/master/GLTFSDK/Source/PBRUtils.cpp#L9 + // https://bghgary.github.io/glTF/convert-between-workflows-bjs/js/babylon.pbrUtilities.js + static float solve_metallic(float p_dielectric_specular, float diffuse, + float specular, + float p_one_minus_specular_strength); + static float get_perceived_brightness(const Color p_color); + static float get_max_component(const Color &p_color); + +public: + void _process_mesh_instances(Ref<GLTFState> state, Node *scene_root); + void _generate_scene_node(Ref<GLTFState> state, Node *scene_parent, + Node3D *scene_root, + const GLTFNodeIndex node_index); + void _import_animation(Ref<GLTFState> state, AnimationPlayer *ap, + const GLTFAnimationIndex index, const int bake_fps); + GLTFMeshIndex _convert_mesh_instance(Ref<GLTFState> state, + MeshInstance3D *p_mesh_instance); + void _convert_mesh_instances(Ref<GLTFState> state); + GLTFCameraIndex _convert_camera(Ref<GLTFState> state, Camera3D *p_camera); + void _convert_light_to_gltf(Light3D *light, Ref<GLTFState> state, Node3D *spatial, Ref<GLTFNode> gltf_node); + GLTFLightIndex _convert_light(Ref<GLTFState> state, Light3D *p_light); + GLTFSkeletonIndex _convert_skeleton(Ref<GLTFState> state, Skeleton3D *p_skeleton); + void _convert_spatial(Ref<GLTFState> state, Node3D *p_spatial, Ref<GLTFNode> p_node); + void _convert_scene_node(Ref<GLTFState> state, Node *p_current, Node *p_root, + const GLTFNodeIndex p_gltf_current, + const GLTFNodeIndex p_gltf_root); + + void _convert_csg_shape_to_gltf(Node *p_current, GLTFNodeIndex p_gltf_parent, Ref<GLTFNode> gltf_node, Ref<GLTFState> state); + + void _create_gltf_node(Ref<GLTFState> state, + Node *p_scene_parent, + GLTFNodeIndex current_node_i, + GLTFNodeIndex p_parent_node_index, + GLTFNodeIndex p_root_gltf_node, + Ref<GLTFNode> gltf_node); + void _convert_animation_player_to_gltf( + AnimationPlayer *animation_player, Ref<GLTFState> state, + const GLTFNodeIndex &p_gltf_current, + const GLTFNodeIndex &p_gltf_root_index, + Ref<GLTFNode> p_gltf_node, Node *p_scene_parent, + Node *p_root); + void _check_visibility(Node *p_node, bool &retflag); + void _convert_camera_to_gltf(Camera3D *camera, Ref<GLTFState> state, + Node3D *spatial, + Ref<GLTFNode> gltf_node); + void _convert_grid_map_to_gltf( + Node *p_scene_parent, + const GLTFNodeIndex &p_parent_node_index, + const GLTFNodeIndex &p_root_node_index, + Ref<GLTFNode> gltf_node, Ref<GLTFState> state, + Node *p_root_node); + void _convert_mult_mesh_instance_to_gltf( + Node *p_scene_parent, + const GLTFNodeIndex &p_parent_node_index, + const GLTFNodeIndex &p_root_node_index, + Ref<GLTFNode> gltf_node, Ref<GLTFState> state, + Node *p_root_node); + void _convert_skeleton_to_gltf( + Node *p_scene_parent, Ref<GLTFState> state, + const GLTFNodeIndex &p_parent_node_index, + const GLTFNodeIndex &p_root_node_index, + Ref<GLTFNode> gltf_node, Node *p_root_node); + void _convert_bone_attachment_to_gltf(Node *p_scene_parent, + Ref<GLTFState> state, + Ref<GLTFNode> gltf_node, + bool &retflag); + void _convert_mesh_to_gltf(Node *p_scene_parent, + Ref<GLTFState> state, Node3D *spatial, + Ref<GLTFNode> gltf_node); + void _convert_animation(Ref<GLTFState> state, AnimationPlayer *ap, + String p_animation_track_name); + Error serialize(Ref<GLTFState> state, Node *p_root, const String &p_path); + Error parse(Ref<GLTFState> state, String p_paths, bool p_read_binary = false); +}; + +#endif // GLTF_DOCUMENT_H diff --git a/modules/gltf/gltf_light.cpp b/modules/gltf/gltf_light.cpp new file mode 100644 index 0000000000..95cca9cf71 --- /dev/null +++ b/modules/gltf/gltf_light.cpp @@ -0,0 +1,101 @@ +/*************************************************************************/ +/* gltf_light.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "gltf_light.h" + +void GLTFLight::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_color"), &GLTFLight::get_color); + ClassDB::bind_method(D_METHOD("set_color", "color"), &GLTFLight::set_color); + ClassDB::bind_method(D_METHOD("get_intensity"), &GLTFLight::get_intensity); + ClassDB::bind_method(D_METHOD("set_intensity", "intensity"), &GLTFLight::set_intensity); + ClassDB::bind_method(D_METHOD("get_type"), &GLTFLight::get_type); + ClassDB::bind_method(D_METHOD("set_type", "type"), &GLTFLight::set_type); + ClassDB::bind_method(D_METHOD("get_range"), &GLTFLight::get_range); + ClassDB::bind_method(D_METHOD("set_range", "range"), &GLTFLight::set_range); + ClassDB::bind_method(D_METHOD("get_inner_cone_angle"), &GLTFLight::get_inner_cone_angle); + ClassDB::bind_method(D_METHOD("set_inner_cone_angle", "inner_cone_angle"), &GLTFLight::set_inner_cone_angle); + ClassDB::bind_method(D_METHOD("get_outer_cone_angle"), &GLTFLight::get_outer_cone_angle); + ClassDB::bind_method(D_METHOD("set_outer_cone_angle", "outer_cone_angle"), &GLTFLight::set_outer_cone_angle); + + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color"); // Color + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "intensity"), "set_intensity", "get_intensity"); // float + ADD_PROPERTY(PropertyInfo(Variant::STRING, "type"), "set_type", "get_type"); // String + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "range"), "set_range", "get_range"); // float + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inner_cone_angle"), "set_inner_cone_angle", "get_inner_cone_angle"); // float + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "outer_cone_angle"), "set_outer_cone_angle", "get_outer_cone_angle"); // float +} + +Color GLTFLight::get_color() { + return color; +} + +void GLTFLight::set_color(Color p_color) { + color = p_color; +} + +float GLTFLight::get_intensity() { + return intensity; +} + +void GLTFLight::set_intensity(float p_intensity) { + intensity = p_intensity; +} + +String GLTFLight::get_type() { + return type; +} + +void GLTFLight::set_type(String p_type) { + type = p_type; +} + +float GLTFLight::get_range() { + return range; +} + +void GLTFLight::set_range(float p_range) { + range = p_range; +} + +float GLTFLight::get_inner_cone_angle() { + return inner_cone_angle; +} + +void GLTFLight::set_inner_cone_angle(float p_inner_cone_angle) { + inner_cone_angle = p_inner_cone_angle; +} + +float GLTFLight::get_outer_cone_angle() { + return outer_cone_angle; +} + +void GLTFLight::set_outer_cone_angle(float p_outer_cone_angle) { + outer_cone_angle = p_outer_cone_angle; +} diff --git a/modules/gltf/gltf_light.h b/modules/gltf/gltf_light.h new file mode 100644 index 0000000000..a859ca1833 --- /dev/null +++ b/modules/gltf/gltf_light.h @@ -0,0 +1,72 @@ +/*************************************************************************/ +/* gltf_light.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef GLTF_LIGHT_H +#define GLTF_LIGHT_H + +#include "core/config/engine.h" +#include "core/io/resource.h" + +class GLTFLight : public Resource { + GDCLASS(GLTFLight, Resource) + friend class GLTFDocument; + +protected: + static void _bind_methods(); + +private: + Color color; + float intensity = 0.0f; + String type; + float range = 0.0f; + float inner_cone_angle = 0.0f; + float outer_cone_angle = 0.0f; + +public: + Color get_color(); + void set_color(Color p_color); + + float get_intensity(); + void set_intensity(float p_intensity); + + String get_type(); + void set_type(String p_type); + + float get_range(); + void set_range(float p_range); + + float get_inner_cone_angle(); + void set_inner_cone_angle(float p_inner_cone_angle); + + float get_outer_cone_angle(); + void set_outer_cone_angle(float p_outer_cone_angle); +}; + +#endif // GLTF_LIGHT_H diff --git a/modules/gltf/gltf_mesh.cpp b/modules/gltf/gltf_mesh.cpp new file mode 100644 index 0000000000..8c10e42c89 --- /dev/null +++ b/modules/gltf/gltf_mesh.cpp @@ -0,0 +1,58 @@ +/*************************************************************************/ +/* gltf_mesh.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "gltf_mesh.h" +#include "editor/import/scene_importer_mesh.h" + +void GLTFMesh::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_mesh"), &GLTFMesh::get_mesh); + ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &GLTFMesh::set_mesh); + ClassDB::bind_method(D_METHOD("get_blend_weights"), &GLTFMesh::get_blend_weights); + ClassDB::bind_method(D_METHOD("set_blend_weights", "blend_weights"), &GLTFMesh::set_blend_weights); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh"), "set_mesh", "get_mesh"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT32_ARRAY, "blend_weights"), "set_blend_weights", "get_blend_weights"); // Vector<float> +} + +Ref<EditorSceneImporterMesh> GLTFMesh::get_mesh() { + return mesh; +} + +void GLTFMesh::set_mesh(Ref<EditorSceneImporterMesh> p_mesh) { + mesh = p_mesh; +} + +Vector<float> GLTFMesh::get_blend_weights() { + return blend_weights; +} + +void GLTFMesh::set_blend_weights(Vector<float> p_blend_weights) { + blend_weights = p_blend_weights; +} diff --git a/modules/gltf/gltf_mesh.h b/modules/gltf/gltf_mesh.h new file mode 100644 index 0000000000..0fc750fc9f --- /dev/null +++ b/modules/gltf/gltf_mesh.h @@ -0,0 +1,55 @@ +/*************************************************************************/ +/* gltf_mesh.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef GLTF_MESH_H +#define GLTF_MESH_H + +#include "core/io/resource.h" +#include "editor/import/resource_importer_scene.h" +#include "editor/import/scene_importer_mesh.h" +#include "scene/resources/mesh.h" + +class GLTFMesh : public Resource { + GDCLASS(GLTFMesh, Resource); + +private: + Ref<EditorSceneImporterMesh> mesh; + Vector<float> blend_weights; + +protected: + static void _bind_methods(); + +public: + Ref<EditorSceneImporterMesh> get_mesh(); + void set_mesh(Ref<EditorSceneImporterMesh> p_mesh); + Vector<float> get_blend_weights(); + void set_blend_weights(Vector<float> p_blend_weights); +}; +#endif // GLTF_MESH_H diff --git a/modules/gltf/gltf_node.cpp b/modules/gltf/gltf_node.cpp new file mode 100644 index 0000000000..777c6fbd9a --- /dev/null +++ b/modules/gltf/gltf_node.cpp @@ -0,0 +1,189 @@ +/*************************************************************************/ +/* gltf_node.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "gltf_node.h" + +void GLTFNode::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_parent"), &GLTFNode::get_parent); + ClassDB::bind_method(D_METHOD("set_parent", "parent"), &GLTFNode::set_parent); + ClassDB::bind_method(D_METHOD("get_height"), &GLTFNode::get_height); + ClassDB::bind_method(D_METHOD("set_height", "height"), &GLTFNode::set_height); + ClassDB::bind_method(D_METHOD("get_xform"), &GLTFNode::get_xform); + ClassDB::bind_method(D_METHOD("set_xform", "xform"), &GLTFNode::set_xform); + ClassDB::bind_method(D_METHOD("get_mesh"), &GLTFNode::get_mesh); + ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &GLTFNode::set_mesh); + ClassDB::bind_method(D_METHOD("get_camera"), &GLTFNode::get_camera); + ClassDB::bind_method(D_METHOD("set_camera", "camera"), &GLTFNode::set_camera); + ClassDB::bind_method(D_METHOD("get_skin"), &GLTFNode::get_skin); + ClassDB::bind_method(D_METHOD("set_skin", "skin"), &GLTFNode::set_skin); + ClassDB::bind_method(D_METHOD("get_skeleton"), &GLTFNode::get_skeleton); + ClassDB::bind_method(D_METHOD("set_skeleton", "skeleton"), &GLTFNode::set_skeleton); + ClassDB::bind_method(D_METHOD("get_joint"), &GLTFNode::get_joint); + ClassDB::bind_method(D_METHOD("set_joint", "joint"), &GLTFNode::set_joint); + ClassDB::bind_method(D_METHOD("get_translation"), &GLTFNode::get_translation); + ClassDB::bind_method(D_METHOD("set_translation", "translation"), &GLTFNode::set_translation); + ClassDB::bind_method(D_METHOD("get_rotation"), &GLTFNode::get_rotation); + ClassDB::bind_method(D_METHOD("set_rotation", "rotation"), &GLTFNode::set_rotation); + ClassDB::bind_method(D_METHOD("get_scale"), &GLTFNode::get_scale); + ClassDB::bind_method(D_METHOD("set_scale", "scale"), &GLTFNode::set_scale); + ClassDB::bind_method(D_METHOD("get_children"), &GLTFNode::get_children); + ClassDB::bind_method(D_METHOD("set_children", "children"), &GLTFNode::set_children); + ClassDB::bind_method(D_METHOD("get_fake_joint_parent"), &GLTFNode::get_fake_joint_parent); + ClassDB::bind_method(D_METHOD("set_fake_joint_parent", "fake_joint_parent"), &GLTFNode::set_fake_joint_parent); + ClassDB::bind_method(D_METHOD("get_light"), &GLTFNode::get_light); + ClassDB::bind_method(D_METHOD("set_light", "light"), &GLTFNode::set_light); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "parent"), "set_parent", "get_parent"); // GLTFNodeIndex + ADD_PROPERTY(PropertyInfo(Variant::INT, "height"), "set_height", "get_height"); // int + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "xform"), "set_xform", "get_xform"); // Transform + ADD_PROPERTY(PropertyInfo(Variant::INT, "mesh"), "set_mesh", "get_mesh"); // GLTFMeshIndex + ADD_PROPERTY(PropertyInfo(Variant::INT, "camera"), "set_camera", "get_camera"); // GLTFCameraIndex + ADD_PROPERTY(PropertyInfo(Variant::INT, "skin"), "set_skin", "get_skin"); // GLTFSkinIndex + ADD_PROPERTY(PropertyInfo(Variant::INT, "skeleton"), "set_skeleton", "get_skeleton"); // GLTFSkeletonIndex + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "joint"), "set_joint", "get_joint"); // bool + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "translation"), "set_translation", "get_translation"); // Vector3 + ADD_PROPERTY(PropertyInfo(Variant::QUAT, "rotation"), "set_rotation", "get_rotation"); // Quat + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "scale"), "set_scale", "get_scale"); // Vector3 + ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "children"), "set_children", "get_children"); // Vector<int> + ADD_PROPERTY(PropertyInfo(Variant::INT, "fake_joint_parent"), "set_fake_joint_parent", "get_fake_joint_parent"); // GLTFNodeIndex + ADD_PROPERTY(PropertyInfo(Variant::INT, "light"), "set_light", "get_light"); // GLTFLightIndex +} + +GLTFNodeIndex GLTFNode::get_parent() { + return parent; +} + +void GLTFNode::set_parent(GLTFNodeIndex p_parent) { + parent = p_parent; +} + +int GLTFNode::get_height() { + return height; +} + +void GLTFNode::set_height(int p_height) { + height = p_height; +} + +Transform GLTFNode::get_xform() { + return xform; +} + +void GLTFNode::set_xform(Transform p_xform) { + xform = p_xform; +} + +GLTFMeshIndex GLTFNode::get_mesh() { + return mesh; +} + +void GLTFNode::set_mesh(GLTFMeshIndex p_mesh) { + mesh = p_mesh; +} + +GLTFCameraIndex GLTFNode::get_camera() { + return camera; +} + +void GLTFNode::set_camera(GLTFCameraIndex p_camera) { + camera = p_camera; +} + +GLTFSkinIndex GLTFNode::get_skin() { + return skin; +} + +void GLTFNode::set_skin(GLTFSkinIndex p_skin) { + skin = p_skin; +} + +GLTFSkeletonIndex GLTFNode::get_skeleton() { + return skeleton; +} + +void GLTFNode::set_skeleton(GLTFSkeletonIndex p_skeleton) { + skeleton = p_skeleton; +} + +bool GLTFNode::get_joint() { + return joint; +} + +void GLTFNode::set_joint(bool p_joint) { + joint = p_joint; +} + +Vector3 GLTFNode::get_translation() { + return translation; +} + +void GLTFNode::set_translation(Vector3 p_translation) { + translation = p_translation; +} + +Quat GLTFNode::get_rotation() { + return rotation; +} + +void GLTFNode::set_rotation(Quat p_rotation) { + rotation = p_rotation; +} + +Vector3 GLTFNode::get_scale() { + return scale; +} + +void GLTFNode::set_scale(Vector3 p_scale) { + scale = p_scale; +} + +Vector<int> GLTFNode::get_children() { + return children; +} + +void GLTFNode::set_children(Vector<int> p_children) { + children = p_children; +} + +GLTFNodeIndex GLTFNode::get_fake_joint_parent() { + return fake_joint_parent; +} + +void GLTFNode::set_fake_joint_parent(GLTFNodeIndex p_fake_joint_parent) { + fake_joint_parent = p_fake_joint_parent; +} + +GLTFLightIndex GLTFNode::get_light() { + return light; +} + +void GLTFNode::set_light(GLTFLightIndex p_light) { + light = p_light; +} diff --git a/modules/gltf/gltf_node.h b/modules/gltf/gltf_node.h new file mode 100644 index 0000000000..ce8aff8944 --- /dev/null +++ b/modules/gltf/gltf_node.h @@ -0,0 +1,105 @@ +/*************************************************************************/ +/* gltf_node.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef GLTF_NODE_H +#define GLTF_NODE_H + +#include "core/io/resource.h" +#include "gltf_document.h" + +class GLTFNode : public Resource { + GDCLASS(GLTFNode, Resource); + friend class GLTFDocument; + friend class PackedSceneGLTF; + +private: + // matrices need to be transformed to this + GLTFNodeIndex parent = -1; + int height = -1; + Transform xform; + GLTFMeshIndex mesh = -1; + GLTFCameraIndex camera = -1; + GLTFSkinIndex skin = -1; + GLTFSkeletonIndex skeleton = -1; + bool joint = false; + Vector3 translation; + Quat rotation; + Vector3 scale = Vector3(1, 1, 1); + Vector<int> children; + GLTFNodeIndex fake_joint_parent = -1; + GLTFLightIndex light = -1; + +protected: + static void _bind_methods(); + +public: + GLTFNodeIndex get_parent(); + void set_parent(GLTFNodeIndex p_parent); + + int get_height(); + void set_height(int p_height); + + Transform get_xform(); + void set_xform(Transform p_xform); + + GLTFMeshIndex get_mesh(); + void set_mesh(GLTFMeshIndex p_mesh); + + GLTFCameraIndex get_camera(); + void set_camera(GLTFCameraIndex p_camera); + + GLTFSkinIndex get_skin(); + void set_skin(GLTFSkinIndex p_skin); + + GLTFSkeletonIndex get_skeleton(); + void set_skeleton(GLTFSkeletonIndex p_skeleton); + + bool get_joint(); + void set_joint(bool p_joint); + + Vector3 get_translation(); + void set_translation(Vector3 p_translation); + + Quat get_rotation(); + void set_rotation(Quat p_rotation); + + Vector3 get_scale(); + void set_scale(Vector3 p_scale); + + Vector<int> get_children(); + void set_children(Vector<int> p_children); + + GLTFNodeIndex get_fake_joint_parent(); + void set_fake_joint_parent(GLTFNodeIndex p_fake_joint_parent); + + GLTFLightIndex get_light(); + void set_light(GLTFLightIndex p_light); +}; +#endif // GLTF_NODE_H diff --git a/modules/gltf/gltf_skeleton.cpp b/modules/gltf/gltf_skeleton.cpp new file mode 100644 index 0000000000..739779d3bd --- /dev/null +++ b/modules/gltf/gltf_skeleton.cpp @@ -0,0 +1,95 @@ +/*************************************************************************/ +/* gltf_skeleton.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "gltf_skeleton.h" + +void GLTFSkeleton::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_joints"), &GLTFSkeleton::get_joints); + ClassDB::bind_method(D_METHOD("set_joints", "joints"), &GLTFSkeleton::set_joints); + ClassDB::bind_method(D_METHOD("get_roots"), &GLTFSkeleton::get_roots); + ClassDB::bind_method(D_METHOD("set_roots", "roots"), &GLTFSkeleton::set_roots); + ClassDB::bind_method(D_METHOD("get_godot_skeleton"), &GLTFSkeleton::get_godot_skeleton); + ClassDB::bind_method(D_METHOD("get_unique_names"), &GLTFSkeleton::get_unique_names); + ClassDB::bind_method(D_METHOD("set_unique_names", "unique_names"), &GLTFSkeleton::set_unique_names); + ClassDB::bind_method(D_METHOD("get_godot_bone_node"), &GLTFSkeleton::get_godot_bone_node); + ClassDB::bind_method(D_METHOD("set_godot_bone_node", "godot_bone_node"), &GLTFSkeleton::set_godot_bone_node); + ClassDB::bind_method(D_METHOD("get_bone_attachment_count"), &GLTFSkeleton::get_bone_attachment_count); + ClassDB::bind_method(D_METHOD("get_bone_attachment"), &GLTFSkeleton::get_bone_attachment); + + ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "joints"), "set_joints", "get_joints"); // Vector<GLTFNodeIndex> + ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "roots"), "set_roots", "get_roots"); // Vector<GLTFNodeIndex> + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "unique_names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_unique_names", "get_unique_names"); // Set<String> + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "godot_bone_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_godot_bone_node", "get_godot_bone_node"); // Map<int32_t, +} + +Vector<GLTFNodeIndex> GLTFSkeleton::get_joints() { + return joints; +} + +void GLTFSkeleton::set_joints(Vector<GLTFNodeIndex> p_joints) { + joints = p_joints; +} + +Vector<GLTFNodeIndex> GLTFSkeleton::get_roots() { + return roots; +} + +void GLTFSkeleton::set_roots(Vector<GLTFNodeIndex> p_roots) { + roots = p_roots; +} + +Skeleton3D *GLTFSkeleton::get_godot_skeleton() { + return godot_skeleton; +} + +Array GLTFSkeleton::get_unique_names() { + return GLTFDocument::to_array(unique_names); +} + +void GLTFSkeleton::set_unique_names(Array p_unique_names) { + GLTFDocument::set_from_array(unique_names, p_unique_names); +} + +Dictionary GLTFSkeleton::get_godot_bone_node() { + return GLTFDocument::to_dict(godot_bone_node); +} + +void GLTFSkeleton::set_godot_bone_node(Dictionary p_indict) { + GLTFDocument::set_from_dict(godot_bone_node, p_indict); +} + +BoneAttachment3D *GLTFSkeleton::get_bone_attachment(int idx) { + ERR_FAIL_INDEX_V(idx, bone_attachments.size(), nullptr); + return bone_attachments[idx]; +} + +int32_t GLTFSkeleton::get_bone_attachment_count() { + return bone_attachments.size(); +} diff --git a/modules/gltf/gltf_skeleton.h b/modules/gltf/gltf_skeleton.h new file mode 100644 index 0000000000..d6986eb35a --- /dev/null +++ b/modules/gltf/gltf_skeleton.h @@ -0,0 +1,101 @@ +/*************************************************************************/ +/* gltf_skeleton.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef GLTF_SKELETON_H +#define GLTF_SKELETON_H + +#include "core/io/resource.h" +#include "gltf_document.h" + +class GLTFSkeleton : public Resource { + GDCLASS(GLTFSkeleton, Resource); + friend class GLTFDocument; + +private: + // The *synthesized* skeletons joints + Vector<GLTFNodeIndex> joints; + + // The roots of the skeleton. If there are multiple, each root must have the + // same parent (ie roots are siblings) + Vector<GLTFNodeIndex> roots; + + // The created Skeleton3D for the scene + Skeleton3D *godot_skeleton = nullptr; + + // Set of unique bone names for the skeleton + Set<String> unique_names; + + Map<int32_t, GLTFNodeIndex> godot_bone_node; + + Vector<BoneAttachment3D *> bone_attachments; + +protected: + static void _bind_methods(); + +public: + Vector<GLTFNodeIndex> get_joints(); + void set_joints(Vector<GLTFNodeIndex> p_joints); + + Vector<GLTFNodeIndex> get_roots(); + void set_roots(Vector<GLTFNodeIndex> p_roots); + + Skeleton3D *get_godot_skeleton(); + + // Skeleton *get_godot_skeleton() { + // return this->godot_skeleton; + // } + // void set_godot_skeleton(Skeleton p_*godot_skeleton) { + // this->godot_skeleton = p_godot_skeleton; + // } + + Array get_unique_names(); + void set_unique_names(Array p_unique_names); + + //Map<int32_t, GLTFNodeIndex> get_godot_bone_node() { + // return this->godot_bone_node; + //} + //void set_godot_bone_node(Map<int32_t, GLTFNodeIndex> p_godot_bone_node) { + // this->godot_bone_node = p_godot_bone_node; + //} + Dictionary get_godot_bone_node(); + void set_godot_bone_node(Dictionary p_indict); + + //Dictionary get_godot_bone_node() { + // return VariantConversion::to_dict(this->godot_bone_node); + //} + //void set_godot_bone_node(Dictionary p_indict) { + // VariantConversion::set_from_dict(this->godot_bone_node, p_indict); + //} + + BoneAttachment3D *get_bone_attachment(int idx); + + int32_t get_bone_attachment_count(); +}; +#endif // GLTF_SKELETON_H diff --git a/modules/gltf/gltf_skin.cpp b/modules/gltf/gltf_skin.cpp new file mode 100644 index 0000000000..fd39e4f45a --- /dev/null +++ b/modules/gltf/gltf_skin.cpp @@ -0,0 +1,155 @@ +/*************************************************************************/ +/* gltf_skin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "gltf_skin.h" + +void GLTFSkin::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_skin_root"), &GLTFSkin::get_skin_root); + ClassDB::bind_method(D_METHOD("set_skin_root", "skin_root"), &GLTFSkin::set_skin_root); + ClassDB::bind_method(D_METHOD("get_joints_original"), &GLTFSkin::get_joints_original); + ClassDB::bind_method(D_METHOD("set_joints_original", "joints_original"), &GLTFSkin::set_joints_original); + ClassDB::bind_method(D_METHOD("get_inverse_binds"), &GLTFSkin::get_inverse_binds); + ClassDB::bind_method(D_METHOD("set_inverse_binds", "inverse_binds"), &GLTFSkin::set_inverse_binds); + ClassDB::bind_method(D_METHOD("get_joints"), &GLTFSkin::get_joints); + ClassDB::bind_method(D_METHOD("set_joints", "joints"), &GLTFSkin::set_joints); + ClassDB::bind_method(D_METHOD("get_non_joints"), &GLTFSkin::get_non_joints); + ClassDB::bind_method(D_METHOD("set_non_joints", "non_joints"), &GLTFSkin::set_non_joints); + ClassDB::bind_method(D_METHOD("get_roots"), &GLTFSkin::get_roots); + ClassDB::bind_method(D_METHOD("set_roots", "roots"), &GLTFSkin::set_roots); + ClassDB::bind_method(D_METHOD("get_skeleton"), &GLTFSkin::get_skeleton); + ClassDB::bind_method(D_METHOD("set_skeleton", "skeleton"), &GLTFSkin::set_skeleton); + ClassDB::bind_method(D_METHOD("get_joint_i_to_bone_i"), &GLTFSkin::get_joint_i_to_bone_i); + ClassDB::bind_method(D_METHOD("set_joint_i_to_bone_i", "joint_i_to_bone_i"), &GLTFSkin::set_joint_i_to_bone_i); + ClassDB::bind_method(D_METHOD("get_joint_i_to_name"), &GLTFSkin::get_joint_i_to_name); + ClassDB::bind_method(D_METHOD("set_joint_i_to_name", "joint_i_to_name"), &GLTFSkin::set_joint_i_to_name); + ClassDB::bind_method(D_METHOD("get_godot_skin"), &GLTFSkin::get_godot_skin); + ClassDB::bind_method(D_METHOD("set_godot_skin", "godot_skin"), &GLTFSkin::set_godot_skin); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "skin_root"), "set_skin_root", "get_skin_root"); // GLTFNodeIndex + ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "joints_original"), "set_joints_original", "get_joints_original"); // Vector<GLTFNodeIndex> + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "inverse_binds", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL), "set_inverse_binds", "get_inverse_binds"); // Vector<Transform> + ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "joints"), "set_joints", "get_joints"); // Vector<GLTFNodeIndex> + ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "non_joints"), "set_non_joints", "get_non_joints"); // Vector<GLTFNodeIndex> + ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "roots"), "set_roots", "get_roots"); // Vector<GLTFNodeIndex> + ADD_PROPERTY(PropertyInfo(Variant::INT, "skeleton"), "set_skeleton", "get_skeleton"); // int + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "joint_i_to_bone_i", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL), "set_joint_i_to_bone_i", "get_joint_i_to_bone_i"); // Map<int, + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "joint_i_to_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL), "set_joint_i_to_name", "get_joint_i_to_name"); // Map<int, + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "godot_skin"), "set_godot_skin", "get_godot_skin"); // Ref<Skin> +} + +GLTFNodeIndex GLTFSkin::get_skin_root() { + return skin_root; +} + +void GLTFSkin::set_skin_root(GLTFNodeIndex p_skin_root) { + skin_root = p_skin_root; +} + +Vector<GLTFNodeIndex> GLTFSkin::get_joints_original() { + return joints_original; +} + +void GLTFSkin::set_joints_original(Vector<GLTFNodeIndex> p_joints_original) { + joints_original = p_joints_original; +} + +Array GLTFSkin::get_inverse_binds() { + return GLTFDocument::to_array(inverse_binds); +} + +void GLTFSkin::set_inverse_binds(Array p_inverse_binds) { + GLTFDocument::set_from_array(inverse_binds, p_inverse_binds); +} + +Vector<GLTFNodeIndex> GLTFSkin::get_joints() { + return joints; +} + +void GLTFSkin::set_joints(Vector<GLTFNodeIndex> p_joints) { + joints = p_joints; +} + +Vector<GLTFNodeIndex> GLTFSkin::get_non_joints() { + return non_joints; +} + +void GLTFSkin::set_non_joints(Vector<GLTFNodeIndex> p_non_joints) { + non_joints = p_non_joints; +} + +Vector<GLTFNodeIndex> GLTFSkin::get_roots() { + return roots; +} + +void GLTFSkin::set_roots(Vector<GLTFNodeIndex> p_roots) { + roots = p_roots; +} + +int GLTFSkin::get_skeleton() { + return skeleton; +} + +void GLTFSkin::set_skeleton(int p_skeleton) { + skeleton = p_skeleton; +} + +Dictionary GLTFSkin::get_joint_i_to_bone_i() { + return GLTFDocument::to_dict(joint_i_to_bone_i); +} + +void GLTFSkin::set_joint_i_to_bone_i(Dictionary p_joint_i_to_bone_i) { + GLTFDocument::set_from_dict(joint_i_to_bone_i, p_joint_i_to_bone_i); +} + +Dictionary GLTFSkin::get_joint_i_to_name() { + Dictionary ret; + Map<int, StringName>::Element *elem = joint_i_to_name.front(); + while (elem) { + ret[elem->key()] = String(elem->value()); + elem = elem->next(); + } + return ret; +} + +void GLTFSkin::set_joint_i_to_name(Dictionary p_joint_i_to_name) { + joint_i_to_name = Map<int, StringName>(); + Array keys = p_joint_i_to_name.keys(); + for (int i = 0; i < keys.size(); i++) { + joint_i_to_name[keys[i]] = joint_i_to_name[keys[i]]; + } +} + +Ref<Skin> GLTFSkin::get_godot_skin() { + return godot_skin; +} + +void GLTFSkin::set_godot_skin(Ref<Skin> p_godot_skin) { + godot_skin = p_godot_skin; +} diff --git a/modules/gltf/gltf_skin.h b/modules/gltf/gltf_skin.h new file mode 100644 index 0000000000..7cc09d85bc --- /dev/null +++ b/modules/gltf/gltf_skin.h @@ -0,0 +1,109 @@ +/*************************************************************************/ +/* gltf_skin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef GLTF_SKIN_H +#define GLTF_SKIN_H + +#include "core/io/resource.h" +#include "gltf_document.h" + +class GLTFSkin : public Resource { + GDCLASS(GLTFSkin, Resource); + friend class GLTFDocument; + +private: + // The "skeleton" property defined in the gltf spec. -1 = Scene Root + GLTFNodeIndex skin_root = -1; + + Vector<GLTFNodeIndex> joints_original; + Vector<Transform> inverse_binds; + + // Note: joints + non_joints should form a complete subtree, or subtrees + // with a common parent + + // All nodes that are skins that are caught in-between the original joints + // (inclusive of joints_original) + Vector<GLTFNodeIndex> joints; + + // All Nodes that are caught in-between skin joint nodes, and are not + // defined as joints by any skin + Vector<GLTFNodeIndex> non_joints; + + // The roots of the skin. In the case of multiple roots, their parent *must* + // be the same (the roots must be siblings) + Vector<GLTFNodeIndex> roots; + + // The GLTF Skeleton this Skin points to (after we determine skeletons) + GLTFSkeletonIndex skeleton = -1; + + // A mapping from the joint indices (in the order of joints_original) to the + // Godot Skeleton's bone_indices + Map<int, int> joint_i_to_bone_i; + Map<int, StringName> joint_i_to_name; + + // The Actual Skin that will be created as a mapping between the IBM's of + // this skin to the generated skeleton for the mesh instances. + Ref<Skin> godot_skin; + +protected: + static void _bind_methods(); + +public: + GLTFNodeIndex get_skin_root(); + void set_skin_root(GLTFNodeIndex p_skin_root); + + Vector<GLTFNodeIndex> get_joints_original(); + void set_joints_original(Vector<GLTFNodeIndex> p_joints_original); + + Array get_inverse_binds(); + void set_inverse_binds(Array p_inverse_binds); + + Vector<GLTFNodeIndex> get_joints(); + void set_joints(Vector<GLTFNodeIndex> p_joints); + + Vector<GLTFNodeIndex> get_non_joints(); + void set_non_joints(Vector<GLTFNodeIndex> p_non_joints); + + Vector<GLTFNodeIndex> get_roots(); + void set_roots(Vector<GLTFNodeIndex> p_roots); + + int get_skeleton(); + void set_skeleton(int p_skeleton); + + Dictionary get_joint_i_to_bone_i(); + void set_joint_i_to_bone_i(Dictionary p_joint_i_to_bone_i); + + Dictionary get_joint_i_to_name(); + void set_joint_i_to_name(Dictionary p_joint_i_to_name); + + Ref<Skin> get_godot_skin(); + void set_godot_skin(Ref<Skin> p_godot_skin); +}; +#endif // GLTF_SKIN_H diff --git a/modules/gltf/gltf_spec_gloss.cpp b/modules/gltf/gltf_spec_gloss.cpp new file mode 100644 index 0000000000..70b182da52 --- /dev/null +++ b/modules/gltf/gltf_spec_gloss.cpp @@ -0,0 +1,90 @@ +/*************************************************************************/ +/* gltf_spec_gloss.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "gltf_spec_gloss.h" + +void GLTFSpecGloss::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_diffuse_img"), &GLTFSpecGloss::get_diffuse_img); + ClassDB::bind_method(D_METHOD("set_diffuse_img", "diffuse_img"), &GLTFSpecGloss::set_diffuse_img); + ClassDB::bind_method(D_METHOD("get_diffuse_factor"), &GLTFSpecGloss::get_diffuse_factor); + ClassDB::bind_method(D_METHOD("set_diffuse_factor", "diffuse_factor"), &GLTFSpecGloss::set_diffuse_factor); + ClassDB::bind_method(D_METHOD("get_gloss_factor"), &GLTFSpecGloss::get_gloss_factor); + ClassDB::bind_method(D_METHOD("set_gloss_factor", "gloss_factor"), &GLTFSpecGloss::set_gloss_factor); + ClassDB::bind_method(D_METHOD("get_specular_factor"), &GLTFSpecGloss::get_specular_factor); + ClassDB::bind_method(D_METHOD("set_specular_factor", "specular_factor"), &GLTFSpecGloss::set_specular_factor); + ClassDB::bind_method(D_METHOD("get_spec_gloss_img"), &GLTFSpecGloss::get_spec_gloss_img); + ClassDB::bind_method(D_METHOD("set_spec_gloss_img", "spec_gloss_img"), &GLTFSpecGloss::set_spec_gloss_img); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "diffuse_img"), "set_diffuse_img", "get_diffuse_img"); // Ref<Image> + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "diffuse_factor"), "set_diffuse_factor", "get_diffuse_factor"); // Color + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gloss_factor"), "set_gloss_factor", "get_gloss_factor"); // float + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "specular_factor"), "set_specular_factor", "get_specular_factor"); // Color + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "spec_gloss_img"), "set_spec_gloss_img", "get_spec_gloss_img"); // Ref<Image> +} + +Ref<Image> GLTFSpecGloss::get_diffuse_img() { + return diffuse_img; +} + +void GLTFSpecGloss::set_diffuse_img(Ref<Image> p_diffuse_img) { + diffuse_img = p_diffuse_img; +} + +Color GLTFSpecGloss::get_diffuse_factor() { + return diffuse_factor; +} + +void GLTFSpecGloss::set_diffuse_factor(Color p_diffuse_factor) { + diffuse_factor = p_diffuse_factor; +} + +float GLTFSpecGloss::get_gloss_factor() { + return gloss_factor; +} + +void GLTFSpecGloss::set_gloss_factor(float p_gloss_factor) { + gloss_factor = p_gloss_factor; +} + +Color GLTFSpecGloss::get_specular_factor() { + return specular_factor; +} + +void GLTFSpecGloss::set_specular_factor(Color p_specular_factor) { + specular_factor = p_specular_factor; +} + +Ref<Image> GLTFSpecGloss::get_spec_gloss_img() { + return spec_gloss_img; +} + +void GLTFSpecGloss::set_spec_gloss_img(Ref<Image> p_spec_gloss_img) { + spec_gloss_img = p_spec_gloss_img; +} diff --git a/modules/gltf/gltf_spec_gloss.h b/modules/gltf/gltf_spec_gloss.h new file mode 100644 index 0000000000..3cc6fb09ed --- /dev/null +++ b/modules/gltf/gltf_spec_gloss.h @@ -0,0 +1,67 @@ +/*************************************************************************/ +/* gltf_spec_gloss.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef GLTF_SPEC_GLOSS_H +#define GLTF_SPEC_GLOSS_H + +#include "core/io/image.h" +#include "core/io/resource.h" + +class GLTFSpecGloss : public Resource { + GDCLASS(GLTFSpecGloss, Resource); + friend class GLTFDocument; + +private: + Ref<Image> diffuse_img = nullptr; + Color diffuse_factor = Color(1.0f, 1.0f, 1.0f); + float gloss_factor = 1.0f; + Color specular_factor = Color(1.0f, 1.0f, 1.0f); + Ref<Image> spec_gloss_img = nullptr; + +protected: + static void _bind_methods(); + +public: + Ref<Image> get_diffuse_img(); + void set_diffuse_img(Ref<Image> p_diffuse_img); + + Color get_diffuse_factor(); + void set_diffuse_factor(Color p_diffuse_factor); + + float get_gloss_factor(); + void set_gloss_factor(float p_gloss_factor); + + Color get_specular_factor(); + void set_specular_factor(Color p_specular_factor); + + Ref<Image> get_spec_gloss_img(); + void set_spec_gloss_img(Ref<Image> p_spec_gloss_img); +}; +#endif // GLTF_SPEC_GLOSS_H diff --git a/modules/gltf/gltf_state.cpp b/modules/gltf/gltf_state.cpp new file mode 100644 index 0000000000..eedc743330 --- /dev/null +++ b/modules/gltf/gltf_state.cpp @@ -0,0 +1,296 @@ +/*************************************************************************/ +/* gltf_state.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "gltf_state.h" + +void GLTFState::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_json"), &GLTFState::get_json); + ClassDB::bind_method(D_METHOD("set_json", "json"), &GLTFState::set_json); + ClassDB::bind_method(D_METHOD("get_major_version"), &GLTFState::get_major_version); + ClassDB::bind_method(D_METHOD("set_major_version", "major_version"), &GLTFState::set_major_version); + ClassDB::bind_method(D_METHOD("get_minor_version"), &GLTFState::get_minor_version); + ClassDB::bind_method(D_METHOD("set_minor_version", "minor_version"), &GLTFState::set_minor_version); + ClassDB::bind_method(D_METHOD("get_glb_data"), &GLTFState::get_glb_data); + ClassDB::bind_method(D_METHOD("set_glb_data", "glb_data"), &GLTFState::set_glb_data); + ClassDB::bind_method(D_METHOD("get_use_named_skin_binds"), &GLTFState::get_use_named_skin_binds); + ClassDB::bind_method(D_METHOD("set_use_named_skin_binds", "use_named_skin_binds"), &GLTFState::set_use_named_skin_binds); + ClassDB::bind_method(D_METHOD("get_nodes"), &GLTFState::get_nodes); + ClassDB::bind_method(D_METHOD("set_nodes", "nodes"), &GLTFState::set_nodes); + ClassDB::bind_method(D_METHOD("get_buffers"), &GLTFState::get_buffers); + ClassDB::bind_method(D_METHOD("set_buffers", "buffers"), &GLTFState::set_buffers); + ClassDB::bind_method(D_METHOD("get_buffer_views"), &GLTFState::get_buffer_views); + ClassDB::bind_method(D_METHOD("set_buffer_views", "buffer_views"), &GLTFState::set_buffer_views); + ClassDB::bind_method(D_METHOD("get_accessors"), &GLTFState::get_accessors); + ClassDB::bind_method(D_METHOD("set_accessors", "accessors"), &GLTFState::set_accessors); + ClassDB::bind_method(D_METHOD("get_meshes"), &GLTFState::get_meshes); + ClassDB::bind_method(D_METHOD("set_meshes", "meshes"), &GLTFState::set_meshes); + ClassDB::bind_method(D_METHOD("get_animation_players_count"), &GLTFState::get_animation_players_count); + ClassDB::bind_method(D_METHOD("get_animation_player"), &GLTFState::get_animation_player); + ClassDB::bind_method(D_METHOD("get_materials"), &GLTFState::get_materials); + ClassDB::bind_method(D_METHOD("set_materials", "materials"), &GLTFState::set_materials); + ClassDB::bind_method(D_METHOD("get_scene_name"), &GLTFState::get_scene_name); + ClassDB::bind_method(D_METHOD("set_scene_name", "scene_name"), &GLTFState::set_scene_name); + ClassDB::bind_method(D_METHOD("get_root_nodes"), &GLTFState::get_root_nodes); + ClassDB::bind_method(D_METHOD("set_root_nodes", "root_nodes"), &GLTFState::set_root_nodes); + ClassDB::bind_method(D_METHOD("get_textures"), &GLTFState::get_textures); + ClassDB::bind_method(D_METHOD("set_textures", "textures"), &GLTFState::set_textures); + ClassDB::bind_method(D_METHOD("get_images"), &GLTFState::get_images); + ClassDB::bind_method(D_METHOD("set_images", "images"), &GLTFState::set_images); + ClassDB::bind_method(D_METHOD("get_skins"), &GLTFState::get_skins); + ClassDB::bind_method(D_METHOD("set_skins", "skins"), &GLTFState::set_skins); + ClassDB::bind_method(D_METHOD("get_cameras"), &GLTFState::get_cameras); + ClassDB::bind_method(D_METHOD("set_cameras", "cameras"), &GLTFState::set_cameras); + ClassDB::bind_method(D_METHOD("get_lights"), &GLTFState::get_lights); + ClassDB::bind_method(D_METHOD("set_lights", "lights"), &GLTFState::set_lights); + ClassDB::bind_method(D_METHOD("get_unique_names"), &GLTFState::get_unique_names); + ClassDB::bind_method(D_METHOD("set_unique_names", "unique_names"), &GLTFState::set_unique_names); + ClassDB::bind_method(D_METHOD("get_skeletons"), &GLTFState::get_skeletons); + ClassDB::bind_method(D_METHOD("set_skeletons", "skeletons"), &GLTFState::set_skeletons); + ClassDB::bind_method(D_METHOD("get_skeleton_to_node"), &GLTFState::get_skeleton_to_node); + ClassDB::bind_method(D_METHOD("set_skeleton_to_node", "skeleton_to_node"), &GLTFState::set_skeleton_to_node); + ClassDB::bind_method(D_METHOD("get_animations"), &GLTFState::get_animations); + ClassDB::bind_method(D_METHOD("set_animations", "animations"), &GLTFState::set_animations); + ClassDB::bind_method(D_METHOD("get_scene_node"), &GLTFState::get_scene_node); + + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "json"), "set_json", "get_json"); // Dictionary + ADD_PROPERTY(PropertyInfo(Variant::INT, "major_version"), "set_major_version", "get_major_version"); // int + ADD_PROPERTY(PropertyInfo(Variant::INT, "minor_version"), "set_minor_version", "get_minor_version"); // int + ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "glb_data"), "set_glb_data", "get_glb_data"); // Vector<uint8_t> + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_named_skin_binds"), "set_use_named_skin_binds", "get_use_named_skin_binds"); // bool + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "nodes", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_nodes", "get_nodes"); // Vector<Ref<GLTFNode>> + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "buffers"), "set_buffers", "get_buffers"); // Vector<Vector<uint8_t> + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "buffer_views", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_buffer_views", "get_buffer_views"); // Vector<Ref<GLTFBufferView>> + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "accessors", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_accessors", "get_accessors"); // Vector<Ref<GLTFAccessor>> + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "meshes", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_meshes", "get_meshes"); // Vector<Ref<GLTFMesh>> + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_materials", "get_materials"); // Vector<Ref<Material> + ADD_PROPERTY(PropertyInfo(Variant::STRING, "scene_name"), "set_scene_name", "get_scene_name"); // String + ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "root_nodes"), "set_root_nodes", "get_root_nodes"); // Vector<int> + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "textures", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_textures", "get_textures"); // Vector<Ref<GLTFTexture>> + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "images", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_images", "get_images"); // Vector<Ref<Texture> + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "skins", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_skins", "get_skins"); // Vector<Ref<GLTFSkin>> + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "cameras", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_cameras", "get_cameras"); // Vector<Ref<GLTFCamera>> + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "lights", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_lights", "get_lights"); // Vector<Ref<GLTFLight>> + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "unique_names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_unique_names", "get_unique_names"); // Set<String> + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "skeletons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_skeletons", "get_skeletons"); // Vector<Ref<GLTFSkeleton>> + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "skeleton_to_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_skeleton_to_node", "get_skeleton_to_node"); // Map<GLTFSkeletonIndex, + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_animations", "get_animations"); // Vector<Ref<GLTFAnimation>> +} + +Dictionary GLTFState::get_json() { + return json; +} + +void GLTFState::set_json(Dictionary p_json) { + json = p_json; +} + +int GLTFState::get_major_version() { + return major_version; +} + +void GLTFState::set_major_version(int p_major_version) { + major_version = p_major_version; +} + +int GLTFState::get_minor_version() { + return minor_version; +} + +void GLTFState::set_minor_version(int p_minor_version) { + minor_version = p_minor_version; +} + +Vector<uint8_t> GLTFState::get_glb_data() { + return glb_data; +} + +void GLTFState::set_glb_data(Vector<uint8_t> p_glb_data) { + glb_data = p_glb_data; +} + +bool GLTFState::get_use_named_skin_binds() { + return use_named_skin_binds; +} + +void GLTFState::set_use_named_skin_binds(bool p_use_named_skin_binds) { + use_named_skin_binds = p_use_named_skin_binds; +} + +Array GLTFState::get_nodes() { + return GLTFDocument::to_array(nodes); +} + +void GLTFState::set_nodes(Array p_nodes) { + GLTFDocument::set_from_array(nodes, p_nodes); +} + +Array GLTFState::get_buffers() { + return GLTFDocument::to_array(buffers); +} + +void GLTFState::set_buffers(Array p_buffers) { + GLTFDocument::set_from_array(buffers, p_buffers); +} + +Array GLTFState::get_buffer_views() { + return GLTFDocument::to_array(buffer_views); +} + +void GLTFState::set_buffer_views(Array p_buffer_views) { + GLTFDocument::set_from_array(buffer_views, p_buffer_views); +} + +Array GLTFState::get_accessors() { + return GLTFDocument::to_array(accessors); +} + +void GLTFState::set_accessors(Array p_accessors) { + GLTFDocument::set_from_array(accessors, p_accessors); +} + +Array GLTFState::get_meshes() { + return GLTFDocument::to_array(meshes); +} + +void GLTFState::set_meshes(Array p_meshes) { + GLTFDocument::set_from_array(meshes, p_meshes); +} + +Array GLTFState::get_materials() { + return GLTFDocument::to_array(materials); +} + +void GLTFState::set_materials(Array p_materials) { + GLTFDocument::set_from_array(materials, p_materials); +} + +String GLTFState::get_scene_name() { + return scene_name; +} + +void GLTFState::set_scene_name(String p_scene_name) { + scene_name = p_scene_name; +} + +Array GLTFState::get_root_nodes() { + return GLTFDocument::to_array(root_nodes); +} + +void GLTFState::set_root_nodes(Array p_root_nodes) { + GLTFDocument::set_from_array(root_nodes, p_root_nodes); +} + +Array GLTFState::get_textures() { + return GLTFDocument::to_array(textures); +} + +void GLTFState::set_textures(Array p_textures) { + GLTFDocument::set_from_array(textures, p_textures); +} + +Array GLTFState::get_images() { + return GLTFDocument::to_array(images); +} + +void GLTFState::set_images(Array p_images) { + GLTFDocument::set_from_array(images, p_images); +} + +Array GLTFState::get_skins() { + return GLTFDocument::to_array(skins); +} + +void GLTFState::set_skins(Array p_skins) { + GLTFDocument::set_from_array(skins, p_skins); +} + +Array GLTFState::get_cameras() { + return GLTFDocument::to_array(cameras); +} + +void GLTFState::set_cameras(Array p_cameras) { + GLTFDocument::set_from_array(cameras, p_cameras); +} + +Array GLTFState::get_lights() { + return GLTFDocument::to_array(lights); +} + +void GLTFState::set_lights(Array p_lights) { + GLTFDocument::set_from_array(lights, p_lights); +} + +Array GLTFState::get_unique_names() { + return GLTFDocument::to_array(unique_names); +} + +void GLTFState::set_unique_names(Array p_unique_names) { + GLTFDocument::set_from_array(unique_names, p_unique_names); +} + +Array GLTFState::get_skeletons() { + return GLTFDocument::to_array(skeletons); +} + +void GLTFState::set_skeletons(Array p_skeletons) { + GLTFDocument::set_from_array(skeletons, p_skeletons); +} + +Dictionary GLTFState::get_skeleton_to_node() { + return GLTFDocument::to_dict(skeleton_to_node); +} + +void GLTFState::set_skeleton_to_node(Dictionary p_skeleton_to_node) { + GLTFDocument::set_from_dict(skeleton_to_node, p_skeleton_to_node); +} + +Array GLTFState::get_animations() { + return GLTFDocument::to_array(animations); +} + +void GLTFState::set_animations(Array p_animations) { + GLTFDocument::set_from_array(animations, p_animations); +} + +Node *GLTFState::get_scene_node(GLTFNodeIndex idx) { + if (!scene_nodes.has(idx)) { + return nullptr; + } + return scene_nodes[idx]; +} + +int GLTFState::get_animation_players_count(int idx) { + return animation_players.size(); +} + +AnimationPlayer *GLTFState::get_animation_player(int idx) { + ERR_FAIL_INDEX_V(idx, animation_players.size(), nullptr); + return animation_players[idx]; +} diff --git a/modules/gltf/gltf_state.h b/modules/gltf/gltf_state.h new file mode 100644 index 0000000000..4ce5aa9491 --- /dev/null +++ b/modules/gltf/gltf_state.h @@ -0,0 +1,180 @@ +/*************************************************************************/ +/* gltf_state.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef GLTF_STATE_H +#define GLTF_STATE_H + +#include "core/io/resource.h" +#include "core/templates/vector.h" +#include "editor_scene_importer_gltf.h" +#include "gltf_accessor.h" +#include "gltf_animation.h" +#include "gltf_buffer_view.h" +#include "gltf_camera.h" +#include "gltf_document.h" +#include "gltf_light.h" +#include "gltf_mesh.h" +#include "gltf_node.h" +#include "gltf_skeleton.h" +#include "gltf_skin.h" +#include "gltf_texture.h" +#include "scene/animation/animation_player.h" +#include "scene/resources/texture.h" + +class GLTFState : public Resource { + GDCLASS(GLTFState, Resource); + friend class GLTFDocument; + friend class PackedSceneGLTF; + + Dictionary json; + int major_version = 0; + int minor_version = 0; + Vector<uint8_t> glb_data; + + bool use_named_skin_binds = false; + + Vector<Ref<GLTFNode>> nodes; + Vector<Vector<uint8_t>> buffers; + Vector<Ref<GLTFBufferView>> buffer_views; + Vector<Ref<GLTFAccessor>> accessors; + + Vector<Ref<GLTFMesh>> meshes; // meshes are loaded directly, no reason not to. + + Vector<AnimationPlayer *> animation_players; + Map<Ref<BaseMaterial3D>, GLTFMaterialIndex> material_cache; + Vector<Ref<BaseMaterial3D>> materials; + + String scene_name; + Vector<int> root_nodes; + Vector<Ref<GLTFTexture>> textures; + Vector<Ref<Texture2D>> images; + + Vector<Ref<GLTFSkin>> skins; + Vector<Ref<GLTFCamera>> cameras; + Vector<Ref<GLTFLight>> lights; + Set<String> unique_names; + + Vector<Ref<GLTFSkeleton>> skeletons; + Map<GLTFSkeletonIndex, GLTFNodeIndex> skeleton_to_node; + Vector<Ref<GLTFAnimation>> animations; + Map<GLTFNodeIndex, Node *> scene_nodes; + +protected: + static void _bind_methods(); + +public: + Dictionary get_json(); + void set_json(Dictionary p_json); + + int get_major_version(); + void set_major_version(int p_major_version); + + int get_minor_version(); + void set_minor_version(int p_minor_version); + + Vector<uint8_t> get_glb_data(); + void set_glb_data(Vector<uint8_t> p_glb_data); + + bool get_use_named_skin_binds(); + void set_use_named_skin_binds(bool p_use_named_skin_binds); + + Array get_nodes(); + void set_nodes(Array p_nodes); + + Array get_buffers(); + void set_buffers(Array p_buffers); + + Array get_buffer_views(); + void set_buffer_views(Array p_buffer_views); + + Array get_accessors(); + void set_accessors(Array p_accessors); + + Array get_meshes(); + void set_meshes(Array p_meshes); + + Array get_materials(); + void set_materials(Array p_materials); + + String get_scene_name(); + void set_scene_name(String p_scene_name); + + Array get_root_nodes(); + void set_root_nodes(Array p_root_nodes); + + Array get_textures(); + void set_textures(Array p_textures); + + Array get_images(); + void set_images(Array p_images); + + Array get_skins(); + void set_skins(Array p_skins); + + Array get_cameras(); + void set_cameras(Array p_cameras); + + Array get_lights(); + void set_lights(Array p_lights); + + Array get_unique_names(); + void set_unique_names(Array p_unique_names); + + Array get_skeletons(); + void set_skeletons(Array p_skeletons); + + Dictionary get_skeleton_to_node(); + void set_skeleton_to_node(Dictionary p_skeleton_to_node); + + Array get_animations(); + void set_animations(Array p_animations); + + Node *get_scene_node(GLTFNodeIndex idx); + + int get_animation_players_count(int idx); + + AnimationPlayer *get_animation_player(int idx); + + //void set_scene_nodes(Map<GLTFNodeIndex, Node *> p_scene_nodes) { + // this->scene_nodes = p_scene_nodes; + //} + + //void set_animation_players(Vector<AnimationPlayer *> p_animation_players) { + // this->animation_players = p_animation_players; + //} + + //Map<Ref<Material>, GLTFMaterialIndex> get_material_cache() { + // return this->material_cache; + //} + //void set_material_cache(Map<Ref<Material>, GLTFMaterialIndex> p_material_cache) { + // this->material_cache = p_material_cache; + //} +}; +#endif // GLTF_STATE_H diff --git a/modules/gltf/gltf_texture.cpp b/modules/gltf/gltf_texture.cpp new file mode 100644 index 0000000000..0482c1064e --- /dev/null +++ b/modules/gltf/gltf_texture.cpp @@ -0,0 +1,46 @@ +/*************************************************************************/ +/* gltf_texture.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "gltf_texture.h" + +void GLTFTexture::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_src_image"), &GLTFTexture::get_src_image); + ClassDB::bind_method(D_METHOD("set_src_image", "src_image"), &GLTFTexture::set_src_image); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "src_image"), "set_src_image", "get_src_image"); // int +} + +GLTFImageIndex GLTFTexture::get_src_image() const { + return src_image; +} + +void GLTFTexture::set_src_image(GLTFImageIndex val) { + src_image = val; +} diff --git a/modules/gltf/gltf_texture.h b/modules/gltf/gltf_texture.h new file mode 100644 index 0000000000..e1d0407fb4 --- /dev/null +++ b/modules/gltf/gltf_texture.h @@ -0,0 +1,51 @@ +/*************************************************************************/ +/* gltf_texture.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef GLTF_TEXTURE_H +#define GLTF_TEXTURE_H + +#include "core/io/resource.h" +#include "gltf_document.h" + +class GLTFTexture : public Resource { + GDCLASS(GLTFTexture, Resource); + +private: + GLTFImageIndex src_image; + +protected: + static void _bind_methods(); + +public: + GLTFImageIndex get_src_image() const; + void set_src_image(GLTFImageIndex val); +}; + +#endif // GLTF_TEXTURE_H diff --git a/modules/gltf/register_types.cpp b/modules/gltf/register_types.cpp new file mode 100644 index 0000000000..dc995c9249 --- /dev/null +++ b/modules/gltf/register_types.cpp @@ -0,0 +1,88 @@ +/*************************************************************************/ +/* register_types.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "register_types.h" + +#include "editor/editor_node.h" +#include "editor_scene_exporter_gltf_plugin.h" +#include "editor_scene_importer_gltf.h" +#include "gltf_accessor.h" +#include "gltf_animation.h" +#include "gltf_buffer_view.h" +#include "gltf_camera.h" +#include "gltf_document.h" +#include "gltf_light.h" +#include "gltf_mesh.h" +#include "gltf_node.h" +#include "gltf_skeleton.h" +#include "gltf_skin.h" +#include "gltf_spec_gloss.h" +#include "gltf_state.h" +#include "gltf_texture.h" + +#ifndef _3D_DISABLED +#ifdef TOOLS_ENABLED +static void _editor_init() { + Ref<EditorSceneImporterGLTF> import_gltf; + import_gltf.instance(); + ResourceImporterScene::get_singleton()->add_importer(import_gltf); +} +#endif +#endif + +void register_gltf_types() { +#ifndef _3D_DISABLED +#ifdef TOOLS_ENABLED + ClassDB::APIType prev_api = ClassDB::get_current_api(); + ClassDB::set_current_api(ClassDB::API_EDITOR); + ClassDB::register_class<EditorSceneImporterGLTF>(); + ClassDB::register_class<GLTFMesh>(); + EditorPlugins::add_by_type<SceneExporterGLTFPlugin>(); + ClassDB::set_current_api(prev_api); + EditorNode::add_init_callback(_editor_init); +#endif + ClassDB::register_class<GLTFSpecGloss>(); + ClassDB::register_class<GLTFNode>(); + ClassDB::register_class<GLTFAnimation>(); + ClassDB::register_class<GLTFBufferView>(); + ClassDB::register_class<GLTFAccessor>(); + ClassDB::register_class<GLTFTexture>(); + ClassDB::register_class<GLTFSkeleton>(); + ClassDB::register_class<GLTFSkin>(); + ClassDB::register_class<GLTFCamera>(); + ClassDB::register_class<GLTFLight>(); + ClassDB::register_class<GLTFState>(); + ClassDB::register_class<GLTFDocument>(); + ClassDB::register_class<PackedSceneGLTF>(); +#endif +} + +void unregister_gltf_types() { +} diff --git a/modules/gltf/register_types.h b/modules/gltf/register_types.h new file mode 100644 index 0000000000..fefacb1106 --- /dev/null +++ b/modules/gltf/register_types.h @@ -0,0 +1,32 @@ +/*************************************************************************/ +/* register_types.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +void register_gltf_types(); +void unregister_gltf_types(); diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index 633b209f0f..5a17541075 100644 --- a/modules/gridmap/grid_map.cpp +++ b/modules/gridmap/grid_map.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h index ca7429ea26..48ad95f9ff 100644 --- a/modules/gridmap/grid_map.h +++ b/modules/gridmap/grid_map.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index 0e6ec7f520..4732a3f62d 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -724,7 +724,7 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In return true; } else { selected_palette = -1; - mesh_library_palette->unselect_all(); + mesh_library_palette->deselect_all(); update_palette(); _update_cursor_instance(); return true; @@ -1030,11 +1030,15 @@ void GridMapEditor::_notification(int p_what) { for (int i = 0; i < 3; i++) { grid[i] = RS::get_singleton()->mesh_create(); grid_instance[i] = RS::get_singleton()->instance_create2(grid[i], get_tree()->get_root()->get_world_3d()->get_scenario()); + RenderingServer::get_singleton()->instance_set_layer_mask(grid_instance[i], 1 << Node3DEditorViewport::MISC_TOOL_LAYER); selection_level_instance[i] = RenderingServer::get_singleton()->instance_create2(selection_level_mesh[i], get_tree()->get_root()->get_world_3d()->get_scenario()); + RenderingServer::get_singleton()->instance_set_layer_mask(selection_level_instance[i], 1 << Node3DEditorViewport::MISC_TOOL_LAYER); } selection_instance = RenderingServer::get_singleton()->instance_create2(selection_mesh, get_tree()->get_root()->get_world_3d()->get_scenario()); + RenderingServer::get_singleton()->instance_set_layer_mask(selection_instance, 1 << Node3DEditorViewport::MISC_TOOL_LAYER); paste_instance = RenderingServer::get_singleton()->instance_create2(paste_mesh, get_tree()->get_root()->get_world_3d()->get_scenario()); + RenderingServer::get_singleton()->instance_set_layer_mask(paste_instance, 1 << Node3DEditorViewport::MISC_TOOL_LAYER); _update_selection_transform(); _update_paste_indicator(); @@ -1284,7 +1288,7 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { info_message->set_align(Label::ALIGN_CENTER); info_message->set_autowrap(true); info_message->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); - info_message->set_anchors_and_margins_preset(PRESET_WIDE, PRESET_MODE_KEEP_SIZE, 8 * EDSCALE); + info_message->set_anchors_and_offsets_preset(PRESET_WIDE, PRESET_MODE_KEEP_SIZE, 8 * EDSCALE); mesh_library_palette->add_child(info_message); edit_axis = Vector3::AXIS_Y; diff --git a/modules/gridmap/grid_map_editor_plugin.h b/modules/gridmap/grid_map_editor_plugin.h index 69c8d999fd..0c0ec64680 100644 --- a/modules/gridmap/grid_map_editor_plugin.h +++ b/modules/gridmap/grid_map_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gridmap/register_types.cpp b/modules/gridmap/register_types.cpp index ab384fa942..5680664213 100644 --- a/modules/gridmap/register_types.cpp +++ b/modules/gridmap/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gridmap/register_types.h b/modules/gridmap/register_types.h index c0e3c39ca8..b977f4c5da 100644 --- a/modules/gridmap/register_types.h +++ b/modules/gridmap/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/hdr/image_loader_hdr.cpp b/modules/hdr/image_loader_hdr.cpp index af3741bae9..9d6a399eff 100644 --- a/modules/hdr/image_loader_hdr.cpp +++ b/modules/hdr/image_loader_hdr.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/hdr/image_loader_hdr.h b/modules/hdr/image_loader_hdr.h index ef8e116616..33fcdd1245 100644 --- a/modules/hdr/image_loader_hdr.h +++ b/modules/hdr/image_loader_hdr.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/hdr/register_types.cpp b/modules/hdr/register_types.cpp index e749928f60..5a4a1993e0 100644 --- a/modules/hdr/register_types.cpp +++ b/modules/hdr/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/hdr/register_types.h b/modules/hdr/register_types.h index 02441516ec..c85bc84dce 100644 --- a/modules/hdr/register_types.h +++ b/modules/hdr/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/icloud/icloud.h b/modules/icloud/icloud.h index 35eede0bf9..7b7aa52b63 100644 --- a/modules/icloud/icloud.h +++ b/modules/icloud/icloud.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/icloud/icloud.mm b/modules/icloud/icloud.mm index 8a8ddbefe9..937ef38018 100644 --- a/modules/icloud/icloud.mm +++ b/modules/icloud/icloud.mm @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/icloud/icloud_module.cpp b/modules/icloud/icloud_module.cpp index 43fdc7d45e..8a2c41a38c 100644 --- a/modules/icloud/icloud_module.cpp +++ b/modules/icloud/icloud_module.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/icloud/icloud_module.h b/modules/icloud/icloud_module.h index 7fd057525e..fb8b5fe66e 100644 --- a/modules/icloud/icloud_module.h +++ b/modules/icloud/icloud_module.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/inappstore/in_app_store.h b/modules/inappstore/in_app_store.h index c8e5d17cec..c66c306319 100644 --- a/modules/inappstore/in_app_store.h +++ b/modules/inappstore/in_app_store.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/inappstore/in_app_store.mm b/modules/inappstore/in_app_store.mm index 62977318c1..427808ae75 100644 --- a/modules/inappstore/in_app_store.mm +++ b/modules/inappstore/in_app_store.mm @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/inappstore/in_app_store_module.cpp b/modules/inappstore/in_app_store_module.cpp index 039bdd4f83..c89735cd1c 100644 --- a/modules/inappstore/in_app_store_module.cpp +++ b/modules/inappstore/in_app_store_module.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/inappstore/in_app_store_module.h b/modules/inappstore/in_app_store_module.h index 44673e58bc..dc38969825 100644 --- a/modules/inappstore/in_app_store_module.h +++ b/modules/inappstore/in_app_store_module.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/jpg/SCsub b/modules/jpg/SCsub index 8ee8e6dd6e..7c6ceeea29 100644 --- a/modules/jpg/SCsub +++ b/modules/jpg/SCsub @@ -6,6 +6,9 @@ Import("env_modules") env_jpg = env_modules.Clone() # Thirdparty source files + +thirdparty_obj = [] + # Not unbundled for now as they are not commonly available as shared library thirdparty_dir = "#thirdparty/jpeg-compressor/" thirdparty_sources = [ @@ -17,7 +20,15 @@ env_jpg.Prepend(CPPPATH=[thirdparty_dir]) env_thirdparty = env_jpg.Clone() env_thirdparty.disable_warnings() -env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) +env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) +env.modules_sources += thirdparty_obj + +# Godot source files + +module_obj = [] + +env_jpg.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj -# Godot's own source files -env_jpg.add_source_files(env.modules_sources, "*.cpp") +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/jpg/image_loader_jpegd.cpp b/modules/jpg/image_loader_jpegd.cpp index e5f041c618..7daf6a3a57 100644 --- a/modules/jpg/image_loader_jpegd.cpp +++ b/modules/jpg/image_loader_jpegd.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/jpg/image_loader_jpegd.h b/modules/jpg/image_loader_jpegd.h index 9aebaad9e3..be265b280c 100644 --- a/modules/jpg/image_loader_jpegd.h +++ b/modules/jpg/image_loader_jpegd.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/jpg/register_types.cpp b/modules/jpg/register_types.cpp index b31746f769..a6ae96635f 100644 --- a/modules/jpg/register_types.cpp +++ b/modules/jpg/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/jpg/register_types.h b/modules/jpg/register_types.h index 52cd378b3a..577e9b06f7 100644 --- a/modules/jpg/register_types.h +++ b/modules/jpg/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/jsonrpc/jsonrpc.cpp b/modules/jsonrpc/jsonrpc.cpp index 320da182f8..306c0ff087 100644 --- a/modules/jsonrpc/jsonrpc.cpp +++ b/modules/jsonrpc/jsonrpc.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -151,7 +151,7 @@ Variant JSONRPC::process_action(const Variant &p_action, bool p_process_arr_elem } String JSONRPC::process_string(const String &p_input) { - if (p_input.empty()) { + if (p_input.is_empty()) { return String(); } diff --git a/modules/jsonrpc/jsonrpc.h b/modules/jsonrpc/jsonrpc.h index c2acf84515..9fd016602d 100644 --- a/modules/jsonrpc/jsonrpc.h +++ b/modules/jsonrpc/jsonrpc.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/jsonrpc/register_types.cpp b/modules/jsonrpc/register_types.cpp index e6affaee41..d6b565ba84 100644 --- a/modules/jsonrpc/register_types.cpp +++ b/modules/jsonrpc/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/jsonrpc/register_types.h b/modules/jsonrpc/register_types.h index 854d73a21f..6a21a12444 100644 --- a/modules/jsonrpc/register_types.h +++ b/modules/jsonrpc/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/lightmapper_rd/lightmapper_rd.cpp b/modules/lightmapper_rd/lightmapper_rd.cpp index 1ef991841b..3067e002d8 100644 --- a/modules/lightmapper_rd/lightmapper_rd.cpp +++ b/modules/lightmapper_rd/lightmapper_rd.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -40,8 +40,8 @@ //#define DEBUG_TEXTURES void LightmapperRD::add_mesh(const MeshData &p_mesh) { - ERR_FAIL_COND(p_mesh.albedo_on_uv2.is_null() || p_mesh.albedo_on_uv2->empty()); - ERR_FAIL_COND(p_mesh.emission_on_uv2.is_null() || p_mesh.emission_on_uv2->empty()); + ERR_FAIL_COND(p_mesh.albedo_on_uv2.is_null() || p_mesh.albedo_on_uv2->is_empty()); + ERR_FAIL_COND(p_mesh.emission_on_uv2.is_null() || p_mesh.emission_on_uv2->is_empty()); ERR_FAIL_COND(p_mesh.albedo_on_uv2->get_width() != p_mesh.emission_on_uv2->get_width()); ERR_FAIL_COND(p_mesh.albedo_on_uv2->get_height() != p_mesh.emission_on_uv2->get_height()); ERR_FAIL_COND(p_mesh.points.size() == 0); @@ -447,7 +447,6 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i if (cell != last_cell) { //cell changed, update pointer to indices giw[cell * 2 + 1] = i; - last_cell = cell; solidw[cell] = true; } tiw[i] = triangle_sort[i].triangle_index; @@ -543,7 +542,7 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i tf.width = grid_size; tf.height = grid_size; tf.depth = grid_size; - tf.type = RD::TEXTURE_TYPE_3D; + tf.texture_type = RD::TEXTURE_TYPE_3D; tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT; Vector<Vector<uint8_t>> texdata; @@ -695,7 +694,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d tf.width = atlas_size.width; tf.height = atlas_size.height; tf.array_layers = atlas_slices; - tf.type = RD::TEXTURE_TYPE_2D_ARRAY; + tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY; tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT; tf.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; @@ -826,84 +825,84 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d { { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.binding = 1; u.ids.push_back(vertex_buffer); base_uniforms.push_back(u); } { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.binding = 2; u.ids.push_back(triangle_buffer); base_uniforms.push_back(u); } { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.binding = 3; u.ids.push_back(box_buffer); base_uniforms.push_back(u); } { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.binding = 4; u.ids.push_back(triangle_cell_indices_buffer); base_uniforms.push_back(u); } { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.binding = 5; u.ids.push_back(lights_buffer); base_uniforms.push_back(u); } { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.binding = 6; u.ids.push_back(seams_buffer); base_uniforms.push_back(u); } { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.binding = 7; u.ids.push_back(probe_positions_buffer); base_uniforms.push_back(u); } { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_TEXTURE; + u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 8; u.ids.push_back(grid_texture); base_uniforms.push_back(u); } { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_TEXTURE; + u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 9; u.ids.push_back(grid_texture_sdf); base_uniforms.push_back(u); } { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_TEXTURE; + u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 10; u.ids.push_back(albedo_array_tex); base_uniforms.push_back(u); } { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_TEXTURE; + u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 11; u.ids.push_back(emission_array_tex); base_uniforms.push_back(u); } { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_SAMPLER; + u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; u.binding = 12; u.ids.push_back(sampler); base_uniforms.push_back(u); @@ -917,7 +916,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d tf.width = atlas_size.width; tf.height = atlas_size.height; tf.depth = 1; - tf.type = RD::TEXTURE_TYPE_2D; + tf.texture_type = RD::TEXTURE_TYPE_2D; tf.usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; tf.format = RD::DATA_FORMAT_D32_SFLOAT; @@ -1049,14 +1048,14 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d { { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_IMAGE; + u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.binding = 0; u.ids.push_back(position_tex); uniforms.push_back(u); } { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_IMAGE; + u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.binding = 1; u.ids.push_back(unocclude_tex); //will be unused uniforms.push_back(u); @@ -1089,42 +1088,42 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d { { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_IMAGE; + u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.binding = 0; u.ids.push_back(light_source_tex); uniforms.push_back(u); } { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_TEXTURE; + u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 1; u.ids.push_back(light_dest_tex); //will be unused uniforms.push_back(u); } { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_TEXTURE; + u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 2; u.ids.push_back(position_tex); uniforms.push_back(u); } { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_TEXTURE; + u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 3; u.ids.push_back(normal_tex); uniforms.push_back(u); } { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_IMAGE; + u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.binding = 4; u.ids.push_back(light_accum_tex); uniforms.push_back(u); } { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_IMAGE; + u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.binding = 5; u.ids.push_back(light_primary_dynamic_tex); uniforms.push_back(u); @@ -1169,49 +1168,49 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d { { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_IMAGE; + u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.binding = 0; u.ids.push_back(light_dest_tex); uniforms.push_back(u); } { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_TEXTURE; + u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 1; u.ids.push_back(light_source_tex); uniforms.push_back(u); } { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_TEXTURE; + u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 2; u.ids.push_back(position_tex); uniforms.push_back(u); } { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_TEXTURE; + u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 3; u.ids.push_back(normal_tex); uniforms.push_back(u); } { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_IMAGE; + u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.binding = 4; u.ids.push_back(light_accum_tex); uniforms.push_back(u); } { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_IMAGE; + u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.binding = 5; u.ids.push_back(unocclude_tex); //reuse unocclude tex uniforms.push_back(u); } { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_TEXTURE; + u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 6; u.ids.push_back(light_environment_tex); //reuse unocclude tex uniforms.push_back(u); @@ -1317,28 +1316,28 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d { { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.binding = 0; u.ids.push_back(light_probe_buffer); uniforms.push_back(u); } { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_TEXTURE; + u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 1; u.ids.push_back(light_dest_tex); uniforms.push_back(u); } { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_TEXTURE; + u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 2; u.ids.push_back(light_primary_dynamic_tex); uniforms.push_back(u); } { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_TEXTURE; + u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 3; u.ids.push_back(light_environment_tex); uniforms.push_back(u); @@ -1463,14 +1462,14 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d { { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_IMAGE; + u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.binding = 0; u.ids.push_back(light_accum_tex); uniforms.push_back(u); } { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_TEXTURE; + u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 1; u.ids.push_back(light_accum_tex2); uniforms.push_back(u); @@ -1554,7 +1553,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d { { RD::Uniform u; - u.type = RD::UNIFORM_TYPE_TEXTURE; + u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 0; u.ids.push_back(light_accum_tex2); uniforms.push_back(u); diff --git a/modules/lightmapper_rd/lightmapper_rd.h b/modules/lightmapper_rd/lightmapper_rd.h index e17b5473a8..bb735baf6c 100644 --- a/modules/lightmapper_rd/lightmapper_rd.h +++ b/modules/lightmapper_rd/lightmapper_rd.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/lightmapper_rd/lm_compute.glsl b/modules/lightmapper_rd/lm_compute.glsl index 56976bd623..8a9adbc5cc 100644 --- a/modules/lightmapper_rd/lm_compute.glsl +++ b/modules/lightmapper_rd/lm_compute.glsl @@ -249,6 +249,15 @@ float quick_hash(vec2 pos) { return fract(sin(dot(pos * 19.19, vec2(49.5791, 97.413))) * 49831.189237); } +float get_omni_attenuation(float distance, float inv_range, float decay) { + float nd = distance * inv_range; + nd *= nd; + nd *= nd; // nd^4 + nd = max(1.0 - nd, 0.0); + nd *= nd; // nd^2 + return nd * pow(max(distance, 0.0001), -decay); +} + void main() { #ifdef MODE_LIGHT_PROBES int probe_index = int(gl_GlobalInvocationID.x); @@ -300,7 +309,7 @@ void main() { d /= lights.data[i].range; - attenuation = pow(max(1.0 - d, 0.0), lights.data[i].attenuation); + attenuation = get_omni_attenuation(d, 1.0 / lights.data[i].range, lights.data[i].attenuation); if (lights.data[i].type == LIGHT_TYPE_SPOT) { vec3 rel = normalize(position - light_pos); diff --git a/modules/lightmapper_rd/register_types.cpp b/modules/lightmapper_rd/register_types.cpp index b16adaf5d6..a7b8c063fd 100644 --- a/modules/lightmapper_rd/register_types.cpp +++ b/modules/lightmapper_rd/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/lightmapper_rd/register_types.h b/modules/lightmapper_rd/register_types.h index b0e15a927f..622d6e37a7 100644 --- a/modules/lightmapper_rd/register_types.h +++ b/modules/lightmapper_rd/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mbedtls/SCsub b/modules/mbedtls/SCsub index 5f5d25a3ee..4fcbe8fb43 100755 --- a/modules/mbedtls/SCsub +++ b/modules/mbedtls/SCsub @@ -5,8 +5,11 @@ Import("env_modules") env_mbed_tls = env_modules.Clone() +# Thirdparty source files + +thirdparty_obj = [] + if env["builtin_mbedtls"]: - # Thirdparty source files thirdparty_sources = [ "aes.c", "aesni.c", @@ -96,7 +99,21 @@ if env["builtin_mbedtls"]: env_thirdparty = env_mbed_tls.Clone() env_thirdparty.disable_warnings() - env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) + env.modules_sources += thirdparty_obj + + +# Godot source files + +module_obj = [] + +env_mbed_tls.add_source_files(module_obj, "*.cpp") + +if env["tests"]: + env_mbed_tls.Append(CPPDEFINES=["TESTS_ENABLED"]) + env_mbed_tls.add_source_files(module_obj, "./tests/*.cpp") + +env.modules_sources += module_obj -# Module sources -env_mbed_tls.add_source_files(env.modules_sources, "*.cpp") +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/mbedtls/crypto_mbedtls.cpp b/modules/mbedtls/crypto_mbedtls.cpp index bec792450a..73931b0365 100644 --- a/modules/mbedtls/crypto_mbedtls.cpp +++ b/modules/mbedtls/crypto_mbedtls.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -44,6 +44,7 @@ #define PEM_END_CRT "-----END CERTIFICATE-----\n" #include <mbedtls/debug.h> +#include <mbedtls/md.h> #include <mbedtls/pem.h> CryptoKey *CryptoKeyMbedTLS::create() { @@ -186,6 +187,68 @@ Error X509CertificateMbedTLS::save(String p_path) { return OK; } +bool HMACContextMbedTLS::is_md_type_allowed(mbedtls_md_type_t p_md_type) { + switch (p_md_type) { + case MBEDTLS_MD_SHA1: + case MBEDTLS_MD_SHA256: + return true; + default: + return false; + } +} + +HMACContext *HMACContextMbedTLS::create() { + return memnew(HMACContextMbedTLS); +} + +Error HMACContextMbedTLS::start(HashingContext::HashType p_hash_type, PackedByteArray p_key) { + ERR_FAIL_COND_V_MSG(ctx != nullptr, ERR_FILE_ALREADY_IN_USE, "HMACContext already started."); + + // HMAC keys can be any size. + ERR_FAIL_COND_V_MSG(p_key.is_empty(), ERR_INVALID_PARAMETER, "Key must not be empty."); + + hash_type = p_hash_type; + mbedtls_md_type_t ht = CryptoMbedTLS::md_type_from_hashtype(p_hash_type, hash_len); + + bool allowed = HMACContextMbedTLS::is_md_type_allowed(ht); + ERR_FAIL_COND_V_MSG(!allowed, ERR_INVALID_PARAMETER, "Unsupported hash type."); + + ctx = memalloc(sizeof(mbedtls_md_context_t)); + mbedtls_md_init((mbedtls_md_context_t *)ctx); + + mbedtls_md_setup((mbedtls_md_context_t *)ctx, mbedtls_md_info_from_type((mbedtls_md_type_t)ht), 1); + int ret = mbedtls_md_hmac_starts((mbedtls_md_context_t *)ctx, (const uint8_t *)p_key.ptr(), (size_t)p_key.size()); + return ret ? FAILED : OK; +} + +Error HMACContextMbedTLS::update(PackedByteArray p_data) { + ERR_FAIL_COND_V_MSG(ctx == nullptr, ERR_INVALID_DATA, "Start must be called before update."); + + ERR_FAIL_COND_V_MSG(p_data.is_empty(), ERR_INVALID_PARAMETER, "Src must not be empty."); + + int ret = mbedtls_md_hmac_update((mbedtls_md_context_t *)ctx, (const uint8_t *)p_data.ptr(), (size_t)p_data.size()); + return ret ? FAILED : OK; +} + +PackedByteArray HMACContextMbedTLS::finish() { + ERR_FAIL_COND_V_MSG(ctx == nullptr, PackedByteArray(), "Start must be called before finish."); + ERR_FAIL_COND_V_MSG(hash_len == 0, PackedByteArray(), "Unsupported hash type."); + + PackedByteArray out; + out.resize(hash_len); + + unsigned char *out_ptr = (unsigned char *)out.ptrw(); + int ret = mbedtls_md_hmac_finish((mbedtls_md_context_t *)ctx, out_ptr); + + mbedtls_md_free((mbedtls_md_context_t *)ctx); + memfree((mbedtls_md_context_t *)ctx); + ctx = nullptr; + hash_len = 0; + + ERR_FAIL_COND_V_MSG(ret, PackedByteArray(), "Error received while finishing HMAC"); + return out; +} + Crypto *CryptoMbedTLS::create() { return memnew(CryptoMbedTLS); } @@ -199,6 +262,7 @@ void CryptoMbedTLS::initialize_crypto() { Crypto::_load_default_certificates = load_default_certificates; X509CertificateMbedTLS::make_default(); CryptoKeyMbedTLS::make_default(); + HMACContextMbedTLS::make_default(); } void CryptoMbedTLS::finalize_crypto() { @@ -210,6 +274,7 @@ void CryptoMbedTLS::finalize_crypto() { } X509CertificateMbedTLS::finalize(); CryptoKeyMbedTLS::finalize(); + HMACContextMbedTLS::finalize(); } CryptoMbedTLS::CryptoMbedTLS() { @@ -313,7 +378,7 @@ PackedByteArray CryptoMbedTLS::generate_random_bytes(int p_bytes) { return out; } -mbedtls_md_type_t CryptoMbedTLS::_md_type_from_hashtype(HashingContext::HashType p_hash_type, int &r_size) { +mbedtls_md_type_t CryptoMbedTLS::md_type_from_hashtype(HashingContext::HashType p_hash_type, int &r_size) { switch (p_hash_type) { case HashingContext::HASH_MD5: r_size = 16; @@ -332,7 +397,7 @@ mbedtls_md_type_t CryptoMbedTLS::_md_type_from_hashtype(HashingContext::HashType Vector<uint8_t> CryptoMbedTLS::sign(HashingContext::HashType p_hash_type, Vector<uint8_t> p_hash, Ref<CryptoKey> p_key) { int size; - mbedtls_md_type_t type = _md_type_from_hashtype(p_hash_type, size); + mbedtls_md_type_t type = CryptoMbedTLS::md_type_from_hashtype(p_hash_type, size); ERR_FAIL_COND_V_MSG(type == MBEDTLS_MD_NONE, Vector<uint8_t>(), "Invalid hash type."); ERR_FAIL_COND_V_MSG(p_hash.size() != size, Vector<uint8_t>(), "Invalid hash provided. Size must be " + itos(size)); Ref<CryptoKeyMbedTLS> key = static_cast<Ref<CryptoKeyMbedTLS>>(p_key); @@ -350,7 +415,7 @@ Vector<uint8_t> CryptoMbedTLS::sign(HashingContext::HashType p_hash_type, Vector bool CryptoMbedTLS::verify(HashingContext::HashType p_hash_type, Vector<uint8_t> p_hash, Vector<uint8_t> p_signature, Ref<CryptoKey> p_key) { int size; - mbedtls_md_type_t type = _md_type_from_hashtype(p_hash_type, size); + mbedtls_md_type_t type = CryptoMbedTLS::md_type_from_hashtype(p_hash_type, size); ERR_FAIL_COND_V_MSG(type == MBEDTLS_MD_NONE, false, "Invalid hash type."); ERR_FAIL_COND_V_MSG(p_hash.size() != size, false, "Invalid hash provided. Size must be " + itos(size)); Ref<CryptoKeyMbedTLS> key = static_cast<Ref<CryptoKeyMbedTLS>>(p_key); diff --git a/modules/mbedtls/crypto_mbedtls.h b/modules/mbedtls/crypto_mbedtls.h index e40ca08643..5ced4d136c 100644 --- a/modules/mbedtls/crypto_mbedtls.h +++ b/modules/mbedtls/crypto_mbedtls.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -101,12 +101,31 @@ public: friend class SSLContextMbedTLS; }; +class HMACContextMbedTLS : public HMACContext { +private: + HashingContext::HashType hash_type; + int hash_len = 0; + void *ctx = nullptr; + +public: + static HMACContext *create(); + static void make_default() { HMACContext::_create = create; } + static void finalize() { HMACContext::_create = nullptr; } + + static bool is_md_type_allowed(mbedtls_md_type_t p_md_type); + + virtual Error start(HashingContext::HashType p_hash_type, PackedByteArray p_key); + virtual Error update(PackedByteArray p_data); + virtual PackedByteArray finish(); + + HMACContextMbedTLS() {} +}; + class CryptoMbedTLS : public Crypto { private: mbedtls_entropy_context entropy; mbedtls_ctr_drbg_context ctr_drbg; static X509CertificateMbedTLS *default_certs; - mbedtls_md_type_t _md_type_from_hashtype(HashingContext::HashType p_hash_type, int &r_size); public: static Crypto *create(); @@ -114,6 +133,7 @@ public: static void finalize_crypto(); static X509CertificateMbedTLS *get_default_certificates(); static void load_default_certificates(String p_path); + static mbedtls_md_type_t md_type_from_hashtype(HashingContext::HashType p_hash_type, int &r_size); virtual PackedByteArray generate_random_bytes(int p_bytes); virtual Ref<CryptoKey> generate_rsa(int p_bytes); diff --git a/modules/mbedtls/dtls_server_mbedtls.cpp b/modules/mbedtls/dtls_server_mbedtls.cpp index d9961b026f..5d895d8579 100644 --- a/modules/mbedtls/dtls_server_mbedtls.cpp +++ b/modules/mbedtls/dtls_server_mbedtls.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mbedtls/dtls_server_mbedtls.h b/modules/mbedtls/dtls_server_mbedtls.h index d93553bf7f..9f0c9670e7 100644 --- a/modules/mbedtls/dtls_server_mbedtls.h +++ b/modules/mbedtls/dtls_server_mbedtls.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mbedtls/packet_peer_mbed_dtls.cpp b/modules/mbedtls/packet_peer_mbed_dtls.cpp index 8206d739ae..b2f8d668bf 100644 --- a/modules/mbedtls/packet_peer_mbed_dtls.cpp +++ b/modules/mbedtls/packet_peer_mbed_dtls.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mbedtls/packet_peer_mbed_dtls.h b/modules/mbedtls/packet_peer_mbed_dtls.h index b958fa3b95..0feec04c6e 100755 --- a/modules/mbedtls/packet_peer_mbed_dtls.h +++ b/modules/mbedtls/packet_peer_mbed_dtls.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mbedtls/register_types.cpp b/modules/mbedtls/register_types.cpp index 84a27c29bd..e483030b94 100644 --- a/modules/mbedtls/register_types.cpp +++ b/modules/mbedtls/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -35,6 +35,10 @@ #include "packet_peer_mbed_dtls.h" #include "stream_peer_mbedtls.h" +#ifdef TESTS_ENABLED +#include "tests/test_crypto_mbedtls.h" +#endif + void register_mbedtls_types() { CryptoMbedTLS::initialize_crypto(); StreamPeerMbedTLS::initialize_ssl(); diff --git a/modules/mbedtls/register_types.h b/modules/mbedtls/register_types.h index 90c81b1682..46ffb8522b 100755 --- a/modules/mbedtls/register_types.h +++ b/modules/mbedtls/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mbedtls/ssl_context_mbedtls.cpp b/modules/mbedtls/ssl_context_mbedtls.cpp index a2200e0644..046f30588a 100644 --- a/modules/mbedtls/ssl_context_mbedtls.cpp +++ b/modules/mbedtls/ssl_context_mbedtls.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mbedtls/ssl_context_mbedtls.h b/modules/mbedtls/ssl_context_mbedtls.h index 96703a7eb7..d243185726 100644 --- a/modules/mbedtls/ssl_context_mbedtls.h +++ b/modules/mbedtls/ssl_context_mbedtls.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mbedtls/stream_peer_mbedtls.cpp b/modules/mbedtls/stream_peer_mbedtls.cpp index e9a610b7ee..1332d0923c 100644 --- a/modules/mbedtls/stream_peer_mbedtls.cpp +++ b/modules/mbedtls/stream_peer_mbedtls.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mbedtls/stream_peer_mbedtls.h b/modules/mbedtls/stream_peer_mbedtls.h index 68b03ae995..ccbbebe4f8 100755 --- a/modules/mbedtls/stream_peer_mbedtls.h +++ b/modules/mbedtls/stream_peer_mbedtls.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mbedtls/tests/test_crypto_mbedtls.cpp b/modules/mbedtls/tests/test_crypto_mbedtls.cpp new file mode 100644 index 0000000000..4217497082 --- /dev/null +++ b/modules/mbedtls/tests/test_crypto_mbedtls.cpp @@ -0,0 +1,62 @@ +/*************************************************************************/ +/* test_crypto_mbedtls.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "modules/mbedtls/tests/test_crypto_mbedtls.h" + +#include "modules/mbedtls/crypto_mbedtls.h" +#include "tests/test_macros.h" + +namespace TestCryptoMbedTLS { + +void hmac_digest_test(HashingContext::HashType ht, String expected_hex) { + CryptoMbedTLS crypto; + PackedByteArray key = String("supersecretkey").to_utf8_buffer(); + PackedByteArray msg = String("Return of the MAC!").to_utf8_buffer(); + PackedByteArray digest = crypto.hmac_digest(ht, key, msg); + String hex = String::hex_encode_buffer(digest.ptr(), digest.size()); + CHECK(hex == expected_hex); +} + +void hmac_context_digest_test(HashingContext::HashType ht, String expected_hex) { + HMACContextMbedTLS ctx; + PackedByteArray key = String("supersecretkey").to_utf8_buffer(); + PackedByteArray msg1 = String("Return of ").to_utf8_buffer(); + PackedByteArray msg2 = String("the MAC!").to_utf8_buffer(); + Error err = ctx.start(ht, key); + CHECK(err == OK); + err = ctx.update(msg1); + CHECK(err == OK); + err = ctx.update(msg2); + CHECK(err == OK); + PackedByteArray digest = ctx.finish(); + String hex = String::hex_encode_buffer(digest.ptr(), digest.size()); + CHECK(hex == expected_hex); +} +} // namespace TestCryptoMbedTLS diff --git a/modules/mbedtls/tests/test_crypto_mbedtls.h b/modules/mbedtls/tests/test_crypto_mbedtls.h new file mode 100644 index 0000000000..b798717e52 --- /dev/null +++ b/modules/mbedtls/tests/test_crypto_mbedtls.h @@ -0,0 +1,61 @@ +/*************************************************************************/ +/* test_crypto_mbedtls.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEST_CRYPTO_MBEDTLS_H +#define TEST_CRYPTO_MBEDTLS_H + +#include "core/crypto/hashing_context.h" + +#include "tests/test_macros.h" + +namespace TestCryptoMbedTLS { + +void hmac_digest_test(HashingContext::HashType ht, String expected_hex); + +TEST_CASE("[CryptoMbedTLS] HMAC digest") { + // SHA-256 + hmac_digest_test(HashingContext::HashType::HASH_SHA256, "fe442023f8a7d36a810e1e7cd8a8e2816457f350a008fbf638296afa12085e59"); + + // SHA-1 + hmac_digest_test(HashingContext::HashType::HASH_SHA1, "a0ac4cd68a2f4812c355983d94e8d025afe7dddf"); +} + +void hmac_context_digest_test(HashingContext::HashType ht, String expected_hex); + +TEST_CASE("[HMACContext] HMAC digest") { + // SHA-256 + hmac_context_digest_test(HashingContext::HashType::HASH_SHA256, "fe442023f8a7d36a810e1e7cd8a8e2816457f350a008fbf638296afa12085e59"); + + // SHA-1 + hmac_context_digest_test(HashingContext::HashType::HASH_SHA1, "a0ac4cd68a2f4812c355983d94e8d025afe7dddf"); +} +} // namespace TestCryptoMbedTLS + +#endif // TEST_CRYPTO_MBEDTLS_H diff --git a/modules/meshoptimizer/SCsub b/modules/meshoptimizer/SCsub new file mode 100644 index 0000000000..3f86bb4f00 --- /dev/null +++ b/modules/meshoptimizer/SCsub @@ -0,0 +1,45 @@ +#!/usr/bin/env python + +Import("env") +Import("env_modules") + +env_meshoptimizer = env_modules.Clone() + +# Thirdparty source files + +thirdparty_obj = [] + +thirdparty_dir = "#thirdparty/meshoptimizer/" +thirdparty_sources = [ + "allocator.cpp", + "clusterizer.cpp", + "indexcodec.cpp", + "indexgenerator.cpp", + "overdrawanalyzer.cpp", + "overdrawoptimizer.cpp", + "simplifier.cpp", + "spatialorder.cpp", + "stripifier.cpp", + "vcacheanalyzer.cpp", + "vcacheoptimizer.cpp", + "vertexcodec.cpp", + "vertexfilter.cpp", + "vfetchanalyzer.cpp", + "vfetchoptimizer.cpp", +] +thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] + +env_thirdparty = env_meshoptimizer.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) +env.modules_sources += thirdparty_obj + +# Godot source files + +module_obj = [] + +env_meshoptimizer.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj + +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/meshoptimizer/config.py b/modules/meshoptimizer/config.py new file mode 100644 index 0000000000..82e4e43397 --- /dev/null +++ b/modules/meshoptimizer/config.py @@ -0,0 +1,7 @@ +def can_build(env, platform): + # Having this on release by default, it's small and a lot of users like to do procedural stuff + return True + + +def configure(env): + pass diff --git a/modules/meshoptimizer/register_types.cpp b/modules/meshoptimizer/register_types.cpp new file mode 100644 index 0000000000..71cd9f4dcb --- /dev/null +++ b/modules/meshoptimizer/register_types.cpp @@ -0,0 +1,43 @@ +/*************************************************************************/ +/* register_types.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "register_types.h" +#include "scene/resources/surface_tool.h" +#include "thirdparty/meshoptimizer/meshoptimizer.h" + +void register_meshoptimizer_types() { + SurfaceTool::optimize_vertex_cache_func = meshopt_optimizeVertexCache; + SurfaceTool::simplify_func = meshopt_simplify; +} + +void unregister_meshoptimizer_types() { + SurfaceTool::optimize_vertex_cache_func = nullptr; + SurfaceTool::simplify_func = nullptr; +} diff --git a/modules/meshoptimizer/register_types.h b/modules/meshoptimizer/register_types.h new file mode 100644 index 0000000000..5b15503acd --- /dev/null +++ b/modules/meshoptimizer/register_types.h @@ -0,0 +1,37 @@ +/*************************************************************************/ +/* register_types.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef MESHOPTIMIZER_REGISTER_TYPES_H +#define MESHOPTIMIZER_REGISTER_TYPES_H + +void register_meshoptimizer_types(); +void unregister_meshoptimizer_types(); + +#endif // PVR_REGISTER_TYPES_H diff --git a/modules/minimp3/SCsub b/modules/minimp3/SCsub new file mode 100644 index 0000000000..f4d1605d55 --- /dev/null +++ b/modules/minimp3/SCsub @@ -0,0 +1,17 @@ +#!/usr/bin/env python + +Import("env") +Import("env_modules") + +env_minimp3 = env_modules.Clone() + +thirdparty_dir = "#thirdparty/minimp3/" + +# Treat minimp3 headers as system headers to avoid raising warnings. Not supported on MSVC. +if not env.msvc: + env_minimp3.Append(CPPFLAGS=["-isystem", Dir(thirdparty_dir).path]) +else: + env_minimp3.Prepend(CPPPATH=[thirdparty_dir]) + +# Godot's own source files +env_minimp3.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/minimp3/audio_stream_mp3.cpp b/modules/minimp3/audio_stream_mp3.cpp new file mode 100644 index 0000000000..8627f71987 --- /dev/null +++ b/modules/minimp3/audio_stream_mp3.cpp @@ -0,0 +1,231 @@ +/*************************************************************************/ +/* audio_stream_mp3.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#define MINIMP3_ONLY_MP3 +#define MINIMP3_FLOAT_OUTPUT +#define MINIMP3_IMPLEMENTATION +#define MINIMP3_NO_STDIO + +#include "audio_stream_mp3.h" + +#include "core/os/file_access.h" + +void AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) { + ERR_FAIL_COND(!active); + + int todo = p_frames; + + while (todo && active) { + mp3dec_frame_info_t frame_info; + mp3d_sample_t *buf_frame = nullptr; + + int samples_mixed = mp3dec_ex_read_frame(mp3d, &buf_frame, &frame_info, mp3_stream->channels); + + if (samples_mixed) { + p_buffer[p_frames - todo] = AudioFrame(buf_frame[0], buf_frame[samples_mixed - 1]); + --todo; + ++frames_mixed; + } + + else { + //EOF + if (mp3_stream->loop) { + seek(mp3_stream->loop_offset); + loops++; + } else { + //fill remainder with silence + for (int i = p_frames - todo; i < p_frames; i++) { + p_buffer[i] = AudioFrame(0, 0); + } + active = false; + todo = 0; + } + } + } +} + +float AudioStreamPlaybackMP3::get_stream_sampling_rate() { + return mp3_stream->sample_rate; +} + +void AudioStreamPlaybackMP3::start(float p_from_pos) { + active = true; + seek(p_from_pos); + loops = 0; + _begin_resample(); +} + +void AudioStreamPlaybackMP3::stop() { + active = false; +} + +bool AudioStreamPlaybackMP3::is_playing() const { + return active; +} + +int AudioStreamPlaybackMP3::get_loop_count() const { + return loops; +} + +float AudioStreamPlaybackMP3::get_playback_position() const { + return float(frames_mixed) / mp3_stream->sample_rate; +} + +void AudioStreamPlaybackMP3::seek(float p_time) { + if (!active) + return; + + if (p_time >= mp3_stream->get_length()) { + p_time = 0; + } + + frames_mixed = uint32_t(mp3_stream->sample_rate * p_time); + mp3dec_ex_seek(mp3d, frames_mixed * mp3_stream->channels); +} + +AudioStreamPlaybackMP3::~AudioStreamPlaybackMP3() { + if (mp3d) { + mp3dec_ex_close(mp3d); + memfree(mp3d); + } +} + +Ref<AudioStreamPlayback> AudioStreamMP3::instance_playback() { + Ref<AudioStreamPlaybackMP3> mp3s; + + ERR_FAIL_COND_V_MSG(data == nullptr, mp3s, + "This AudioStreamMP3 does not have an audio file assigned " + "to it. AudioStreamMP3 should not be created from the " + "inspector or with `.new()`. Instead, load an audio file."); + + mp3s.instance(); + mp3s->mp3_stream = Ref<AudioStreamMP3>(this); + mp3s->mp3d = (mp3dec_ex_t *)memalloc(sizeof(mp3dec_ex_t)); + + int errorcode = mp3dec_ex_open_buf(mp3s->mp3d, (const uint8_t *)data, data_len, MP3D_SEEK_TO_SAMPLE); + + mp3s->frames_mixed = 0; + mp3s->active = false; + mp3s->loops = 0; + + if (errorcode) { + ERR_FAIL_COND_V(errorcode, Ref<AudioStreamPlaybackMP3>()); + } + + return mp3s; +} + +String AudioStreamMP3::get_stream_name() const { + return ""; //return stream_name; +} + +void AudioStreamMP3::clear_data() { + if (data) { + memfree(data); + data = nullptr; + data_len = 0; + } +} + +void AudioStreamMP3::set_data(const Vector<uint8_t> &p_data) { + int src_data_len = p_data.size(); + const uint8_t *src_datar = p_data.ptr(); + + mp3dec_ex_t mp3d; + mp3dec_ex_open_buf(&mp3d, src_datar, src_data_len, MP3D_SEEK_TO_SAMPLE); + + channels = mp3d.info.channels; + sample_rate = mp3d.info.hz; + length = float(mp3d.samples) / (sample_rate * float(channels)); + + mp3dec_ex_close(&mp3d); + + clear_data(); + + data = memalloc(src_data_len); + copymem(data, src_datar, src_data_len); + data_len = src_data_len; +} + +Vector<uint8_t> AudioStreamMP3::get_data() const { + Vector<uint8_t> vdata; + + if (data_len && data) { + vdata.resize(data_len); + { + uint8_t *w = vdata.ptrw(); + copymem(w, data, data_len); + } + } + + return vdata; +} + +void AudioStreamMP3::set_loop(bool p_enable) { + loop = p_enable; +} + +bool AudioStreamMP3::has_loop() const { + return loop; +} + +void AudioStreamMP3::set_loop_offset(float p_seconds) { + loop_offset = p_seconds; +} + +float AudioStreamMP3::get_loop_offset() const { + return loop_offset; +} + +float AudioStreamMP3::get_length() const { + return length; +} + +void AudioStreamMP3::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamMP3::set_data); + ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamMP3::get_data); + + ClassDB::bind_method(D_METHOD("set_loop", "enable"), &AudioStreamMP3::set_loop); + ClassDB::bind_method(D_METHOD("has_loop"), &AudioStreamMP3::has_loop); + + ClassDB::bind_method(D_METHOD("set_loop_offset", "seconds"), &AudioStreamMP3::set_loop_offset); + ClassDB::bind_method(D_METHOD("get_loop_offset"), &AudioStreamMP3::get_loop_offset); + + ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_data", "get_data"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_loop", "has_loop"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "loop_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_loop_offset", "get_loop_offset"); +} + +AudioStreamMP3::AudioStreamMP3() { +} + +AudioStreamMP3::~AudioStreamMP3() { + clear_data(); +} diff --git a/modules/minimp3/audio_stream_mp3.h b/modules/minimp3/audio_stream_mp3.h new file mode 100644 index 0000000000..de02ba6003 --- /dev/null +++ b/modules/minimp3/audio_stream_mp3.h @@ -0,0 +1,110 @@ +/*************************************************************************/ +/* audio_stream_mp3.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef AUDIO_STREAM_MP3_H +#define AUDIO_STREAM_MP3_H + +#include "core/io/resource_loader.h" +#include "servers/audio/audio_stream.h" + +#include "minimp3_ex.h" + +class AudioStreamMP3; + +class AudioStreamPlaybackMP3 : public AudioStreamPlaybackResampled { + GDCLASS(AudioStreamPlaybackMP3, AudioStreamPlaybackResampled); + + mp3dec_ex_t *mp3d = nullptr; + uint32_t frames_mixed = 0; + bool active = false; + int loops = 0; + + friend class AudioStreamMP3; + + Ref<AudioStreamMP3> mp3_stream; + +protected: + virtual void _mix_internal(AudioFrame *p_buffer, int p_frames) override; + virtual float get_stream_sampling_rate() override; + +public: + virtual void start(float p_from_pos = 0.0) override; + virtual void stop() override; + virtual bool is_playing() const override; + + virtual int get_loop_count() const override; //times it looped + + virtual float get_playback_position() const override; + virtual void seek(float p_time) override; + + AudioStreamPlaybackMP3() {} + ~AudioStreamPlaybackMP3(); +}; + +class AudioStreamMP3 : public AudioStream { + GDCLASS(AudioStreamMP3, AudioStream); + OBJ_SAVE_TYPE(AudioStream) //children are all saved as AudioStream, so they can be exchanged + RES_BASE_EXTENSION("mp3str"); + + friend class AudioStreamPlaybackMP3; + + void *data = nullptr; + uint32_t data_len = 0; + + float sample_rate = 1; + int channels = 1; + float length = 0; + bool loop = false; + float loop_offset = 0; + void clear_data(); + +protected: + static void _bind_methods(); + +public: + void set_loop(bool p_enable); + bool has_loop() const; + + void set_loop_offset(float p_seconds); + float get_loop_offset() const; + + virtual Ref<AudioStreamPlayback> instance_playback() override; + virtual String get_stream_name() const override; + + void set_data(const Vector<uint8_t> &p_data); + Vector<uint8_t> get_data() const; + + virtual float get_length() const override; + + AudioStreamMP3(); + virtual ~AudioStreamMP3(); +}; + +#endif // AUDIO_STREAM_MP3_H diff --git a/modules/minimp3/config.py b/modules/minimp3/config.py new file mode 100644 index 0000000000..bd35d099b9 --- /dev/null +++ b/modules/minimp3/config.py @@ -0,0 +1,16 @@ +def can_build(env, platform): + return True + + +def configure(env): + pass + + +def get_doc_classes(): + return [ + "AudioStreamMP3", + ] + + +def get_doc_path(): + return "doc_classes" diff --git a/modules/minimp3/doc_classes/AudioStreamMP3.xml b/modules/minimp3/doc_classes/AudioStreamMP3.xml new file mode 100644 index 0000000000..92e777ca0f --- /dev/null +++ b/modules/minimp3/doc_classes/AudioStreamMP3.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="AudioStreamMP3" inherits="AudioStream" version="4.0"> + <brief_description> + MP3 audio stream driver. + </brief_description> + <description> + MP3 audio stream driver. + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <members> + <member name="data" type="PackedByteArray" setter="set_data" getter="get_data" default="PackedByteArray( )"> + Contains the audio data in bytes. + </member> + <member name="loop" type="bool" setter="set_loop" getter="has_loop" default="false"> + If [code]true[/code], the stream will automatically loop when it reaches the end. + </member> + <member name="loop_offset" type="float" setter="set_loop_offset" getter="get_loop_offset" default="0.0"> + Time in seconds at which the stream starts after being looped. + </member> + </members> + <constants> + </constants> +</class> diff --git a/modules/minimp3/register_types.cpp b/modules/minimp3/register_types.cpp new file mode 100644 index 0000000000..4ab4c743d6 --- /dev/null +++ b/modules/minimp3/register_types.cpp @@ -0,0 +1,52 @@ +/*************************************************************************/ +/* register_types.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "register_types.h" + +#include "audio_stream_mp3.h" + +#ifdef TOOLS_ENABLED +#include "core/config/engine.h" +#include "resource_importer_mp3.h" +#endif + +void register_minimp3_types() { +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + Ref<ResourceImporterMP3> mp3_import; + mp3_import.instance(); + ResourceFormatImporter::get_singleton()->add_importer(mp3_import); + } +#endif + ClassDB::register_class<AudioStreamMP3>(); +} + +void unregister_minimp3_types() { +} diff --git a/modules/minimp3/register_types.h b/modules/minimp3/register_types.h new file mode 100644 index 0000000000..96227c272e --- /dev/null +++ b/modules/minimp3/register_types.h @@ -0,0 +1,37 @@ +/*************************************************************************/ +/* register_types.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef MINIMP3_REGISTER_TYPES_H +#define MINIMP3_REGISTER_TYPES_H + +void register_minimp3_types(); +void unregister_minimp3_types(); + +#endif // MINIMP3_REGISTER_TYPES_H diff --git a/modules/minimp3/resource_importer_mp3.cpp b/modules/minimp3/resource_importer_mp3.cpp new file mode 100644 index 0000000000..afd26fb79e --- /dev/null +++ b/modules/minimp3/resource_importer_mp3.cpp @@ -0,0 +1,104 @@ +/*************************************************************************/ +/* resource_importer_mp3.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "resource_importer_mp3.h" + +#include "core/io/resource_saver.h" +#include "core/os/file_access.h" +#include "scene/resources/texture.h" + +String ResourceImporterMP3::get_importer_name() const { + return "mp3"; +} + +String ResourceImporterMP3::get_visible_name() const { + return "MP3"; +} + +void ResourceImporterMP3::get_recognized_extensions(List<String> *p_extensions) const { + p_extensions->push_back("mp3"); +} + +String ResourceImporterMP3::get_save_extension() const { + return "mp3str"; +} + +String ResourceImporterMP3::get_resource_type() const { + return "AudioStreamMP3"; +} + +bool ResourceImporterMP3::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const { + return true; +} + +int ResourceImporterMP3::get_preset_count() const { + return 0; +} + +String ResourceImporterMP3::get_preset_name(int p_idx) const { + return String(); +} + +void ResourceImporterMP3::get_import_options(List<ImportOption> *r_options, int p_preset) const { + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), true)); + r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "loop_offset"), 0)); +} + +Error ResourceImporterMP3::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { + bool loop = p_options["loop"]; + float loop_offset = p_options["loop_offset"]; + + FileAccess *f = FileAccess::open(p_source_file, FileAccess::READ); + + ERR_FAIL_COND_V(!f, ERR_CANT_OPEN); + + size_t len = f->get_len(); + + Vector<uint8_t> data; + data.resize(len); + uint8_t *w = data.ptrw(); + + f->get_buffer(w, len); + + memdelete(f); + + Ref<AudioStreamMP3> mp3_stream; + mp3_stream.instance(); + + mp3_stream->set_data(data); + ERR_FAIL_COND_V(!mp3_stream->get_data().size(), ERR_FILE_CORRUPT); + mp3_stream->set_loop(loop); + mp3_stream->set_loop_offset(loop_offset); + + return ResourceSaver::save(p_save_path + ".mp3str", mp3_stream); +} + +ResourceImporterMP3::ResourceImporterMP3() { +} diff --git a/modules/minimp3/resource_importer_mp3.h b/modules/minimp3/resource_importer_mp3.h new file mode 100644 index 0000000000..71b51887a2 --- /dev/null +++ b/modules/minimp3/resource_importer_mp3.h @@ -0,0 +1,58 @@ +/*************************************************************************/ +/* resource_importer_mp3.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RESOURCE_IMPORTER_MP3_H +#define RESOURCE_IMPORTER_MP3_H + +#include "audio_stream_mp3.h" +#include "core/io/resource_importer.h" + +class ResourceImporterMP3 : public ResourceImporter { + GDCLASS(ResourceImporterMP3, ResourceImporter); + +public: + virtual String get_importer_name() const override; + virtual String get_visible_name() const override; + virtual void get_recognized_extensions(List<String> *p_extensions) const override; + virtual String get_save_extension() const override; + virtual String get_resource_type() const override; + + virtual int get_preset_count() const override; + virtual String get_preset_name(int p_idx) const override; + + virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const override; + virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const override; + + virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + + ResourceImporterMP3(); +}; + +#endif // RESOURCE_IMPORTER_MP3_H diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp index a2fb443ef0..a9073ea4a0 100644 --- a/modules/mobile_vr/mobile_vr_interface.cpp +++ b/modules/mobile_vr/mobile_vr_interface.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mobile_vr/mobile_vr_interface.h b/modules/mobile_vr/mobile_vr_interface.h index 9b03fff777..1afa6c39b6 100644 --- a/modules/mobile_vr/mobile_vr_interface.h +++ b/modules/mobile_vr/mobile_vr_interface.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mobile_vr/register_types.cpp b/modules/mobile_vr/register_types.cpp index 0bb555e780..e7d33ba8a7 100644 --- a/modules/mobile_vr/register_types.cpp +++ b/modules/mobile_vr/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mobile_vr/register_types.h b/modules/mobile_vr/register_types.h index 33f608b6ed..9f20f252a4 100644 --- a/modules/mobile_vr/register_types.h +++ b/modules/mobile_vr/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/build_scripts/make_android_mono_config.py b/modules/mono/build_scripts/make_android_mono_config.py index 04f8c80243..28494bff6e 100644 --- a/modules/mono/build_scripts/make_android_mono_config.py +++ b/modules/mono/build_scripts/make_android_mono_config.py @@ -38,10 +38,10 @@ String get_godot_android_mono_config() { Vector<uint8_t> data; data.resize(config_uncompressed_size); uint8_t* w = data.ptrw(); - Compression::decompress(w.ptr(), config_uncompressed_size, config_compressed_data, + Compression::decompress(w, config_uncompressed_size, config_compressed_data, config_compressed_size, Compression::MODE_DEFLATE); String s; - if (s.parse_utf8((const char *)w.ptr(), data.size())) { + if (s.parse_utf8((const char *)w, data.size())) { ERR_FAIL_V(String()); } return s; diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py index 6057004166..309abfbff7 100644 --- a/modules/mono/build_scripts/mono_configure.py +++ b/modules/mono/build_scripts/mono_configure.py @@ -97,6 +97,7 @@ def configure(env, env_mono): copy_mono_root = env["copy_mono_root"] mono_prefix = env["mono_prefix"] + mono_bcl = env["mono_bcl"] mono_lib_names = ["mono-2.0-sgen", "monosgen-2.0"] @@ -262,7 +263,8 @@ def configure(env, env_mono): env_mono.Append(CPPDEFINES=["_REENTRANT"]) if mono_static: - env.Append(LINKFLAGS=["-rdynamic"]) + if not is_javascript: + env.Append(LINKFLAGS=["-rdynamic"]) mono_lib_file = os.path.join(mono_lib_path, "lib" + mono_lib + ".a") @@ -397,7 +399,7 @@ def configure(env, env_mono): if tools_enabled: # Only supported for editor builds. - copy_mono_root_files(env, mono_root) + copy_mono_root_files(env, mono_root, mono_bcl) def make_template_dir(env, mono_root): @@ -430,7 +432,7 @@ def make_template_dir(env, mono_root): copy_mono_shared_libs(env, mono_root, template_mono_root_dir) -def copy_mono_root_files(env, mono_root): +def copy_mono_root_files(env, mono_root, mono_bcl): from glob import glob from shutil import copy from shutil import rmtree @@ -455,7 +457,7 @@ def copy_mono_root_files(env, mono_root): # Copy framework assemblies - mono_framework_dir = os.path.join(mono_root, "lib", "mono", "4.5") + mono_framework_dir = mono_bcl or os.path.join(mono_root, "lib", "mono", "4.5") mono_framework_facades_dir = os.path.join(mono_framework_dir, "Facades") editor_mono_framework_dir = os.path.join(editor_mono_root_dir, "lib", "mono", "4.5") diff --git a/modules/mono/class_db_api_json.cpp b/modules/mono/class_db_api_json.cpp index e3119a8da7..553c6eca53 100644 --- a/modules/mono/class_db_api_json.cpp +++ b/modules/mono/class_db_api_json.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -71,7 +71,7 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) { while ((k = t->method_map.next(k))) { String name = k->operator String(); - ERR_CONTINUE(name.empty()); + ERR_CONTINUE(name.is_empty()); if (name[0] == '_') { continue; // Ignore non-virtual methods that start with an underscore @@ -122,7 +122,7 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) { method_dict["hint_flags"] = mb->get_hint_flags(); } - if (!methods.empty()) { + if (!methods.is_empty()) { class_dict["methods"] = methods; } } @@ -149,7 +149,7 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) { constant_dict["value"] = t->constant_map[F->get()]; } - if (!constants.empty()) { + if (!constants.is_empty()) { class_dict["constants"] = constants; } } @@ -184,7 +184,7 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) { } } - if (!signals.empty()) { + if (!signals.is_empty()) { class_dict["signals"] = signals; } } @@ -214,7 +214,7 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) { property_dict["getter"] = psg->getter; } - if (!properties.empty()) { + if (!properties.is_empty()) { class_dict["property_setget"] = properties; } } @@ -233,7 +233,7 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) { property_dict["usage"] = F->get().usage; } - if (!property_list.empty()) { + if (!property_list.is_empty()) { class_dict["property_list"] = property_list; } } diff --git a/modules/mono/class_db_api_json.h b/modules/mono/class_db_api_json.h index 6b7f5a4d88..6698a6260f 100644 --- a/modules/mono/class_db_api_json.h +++ b/modules/mono/class_db_api_json.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/config.py b/modules/mono/config.py index c4a6b408e6..4c851a2989 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -27,6 +27,14 @@ def configure(env): PathVariable.PathAccept, ) ) + envvars.Add( + PathVariable( + "mono_bcl", + "Path to a custom Mono BCL (Base Class Library) directory for the target platform", + "", + PathVariable.PathAccept, + ) + ) envvars.Add(BoolVariable("mono_static", "Statically link Mono", default_mono_static)) envvars.Add(BoolVariable("mono_glue", "Build with the Mono glue sources", True)) envvars.Add(BoolVariable("build_cil", "Build C# solutions", True)) diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index b4537f531d..da4ece8c5c 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -346,14 +346,18 @@ Ref<Script> CSharpLanguage::get_template(const String &p_class_name, const Strin "// }\n" "}\n"; - String base_class_name = get_base_class_name(p_base_class_name, p_class_name); + // Replaces all spaces in p_class_name with underscores to prevent + // erronous C# Script templates from being generated when the object name + // has spaces in it. + String class_name_no_spaces = p_class_name.replace(" ", "_"); + String base_class_name = get_base_class_name(p_base_class_name, class_name_no_spaces); script_template = script_template.replace("%BASE%", base_class_name) - .replace("%CLASS%", p_class_name); + .replace("%CLASS%", class_name_no_spaces); Ref<CSharpScript> script; script.instance(); script->set_source_code(script_template); - script->set_name(p_class_name); + script->set_name(class_name_no_spaces); return script; } @@ -364,9 +368,10 @@ bool CSharpLanguage::is_using_templates() { void CSharpLanguage::make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) { String src = p_script->get_source_code(); - String base_class_name = get_base_class_name(p_base_class_name, p_class_name); + String class_name_no_spaces = p_class_name.replace(" ", "_"); + String base_class_name = get_base_class_name(p_base_class_name, class_name_no_spaces); src = src.replace("%BASE%", base_class_name) - .replace("%CLASS%", p_class_name) + .replace("%CLASS%", class_name_no_spaces) .replace("%TS%", _get_indentation()); p_script->set_source_code(src); } @@ -395,7 +400,7 @@ bool CSharpLanguage::supports_builtin_mode() const { #ifdef TOOLS_ENABLED static String variant_type_to_managed_name(const String &p_var_type_name) { - if (p_var_type_name.empty()) { + if (p_var_type_name.is_empty()) { return "object"; } @@ -757,7 +762,7 @@ bool CSharpLanguage::is_assembly_reloading_needed() { String appname = ProjectSettings::get_singleton()->get("application/config/name"); String appname_safe = OS::get_singleton()->get_safe_dir_name(appname); - if (appname_safe.empty()) { + if (appname_safe.is_empty()) { appname_safe = "UnnamedProject"; } @@ -854,7 +859,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { to_reload.push_back(script); - if (script->get_path().empty()) { + if (script->get_path().is_empty()) { script->tied_class_name_for_reload = script->script_class->get_name_for_lookup(); script->tied_class_namespace_for_reload = script->script_class->get_namespace(); } @@ -971,7 +976,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { #endif script->signals_invalidated = true; - if (!script->get_path().empty()) { + if (!script->get_path().is_empty()) { script->reload(p_soft_reload); if (!script->valid) { @@ -1441,7 +1446,7 @@ Map<Object *, CSharpScriptBinding>::Element *CSharpLanguage::insert_script_bindi void CSharpLanguage::free_instance_binding_data(void *p_data) { if (GDMono::get_singleton() == nullptr) { #ifdef DEBUG_ENABLED - CRASH_COND(!script_bindings.empty()); + CRASH_COND(!script_bindings.is_empty()); #endif // Mono runtime finalized, all the gchandle bindings were already released return; @@ -2984,7 +2989,7 @@ void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) { p_script->tool = nesting_class && nesting_class->has_attribute(CACHED_CLASS(ToolAttribute)); } -#if TOOLS_ENABLED +#ifdef TOOLS_ENABLED if (!p_script->tool) { p_script->tool = p_script->script_class->get_assembly() == GDMono::get_singleton()->get_tools_assembly(); } @@ -3123,7 +3128,7 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg ERR_FAIL_COND_V_MSG(p_argcount == 0, nullptr, "Cannot create script instance. The class '" + script_class->get_full_name() + "' does not define a parameterless constructor." + - (get_path().empty() ? String() : " Path: '" + get_path() + "'.")); + (get_path().is_empty() ? String() : " Path: '" + get_path() + "'.")); ERR_FAIL_V_MSG(nullptr, "Constructor not found."); } @@ -3278,7 +3283,7 @@ bool CSharpScript::instance_has(const Object *p_this) const { } bool CSharpScript::has_source_code() const { - return !source.empty(); + return !source.is_empty(); } String CSharpScript::get_source_code() const { @@ -3593,7 +3598,7 @@ Error CSharpScript::load_source_code(const String &p_path) { void CSharpScript::_update_name() { String path = get_path(); - if (!path.empty()) { + if (!path.is_empty()) { name = get_path().get_file().get_basename(); } } diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index 3e4e6c3f86..85edd8b9c6 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,6 +31,7 @@ #ifndef CSHARP_SCRIPT_H #define CSHARP_SCRIPT_H +#include "core/doc_data.h" #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" #include "core/object/script_language.h" @@ -189,6 +190,14 @@ public: String get_source_code() const override; void set_source_code(const String &p_code) override; +#ifdef TOOLS_ENABLED + virtual const Vector<DocData::ClassDoc> &get_documentation() const override { + // TODO + static Vector<DocData::ClassDoc> docs; + return docs; + } +#endif // TOOLS_ENABLED + Error reload(bool p_keep_state = false) override; bool has_script_signal(const StringName &p_signal) const override; diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs index 9514cc9622..1a1639aac7 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs @@ -2,6 +2,7 @@ using Godot; using System; using Godot.Collections; using GodotTools.Internals; +using JetBrains.Annotations; using File = GodotTools.Utils.File; using Path = System.IO.Path; @@ -26,6 +27,9 @@ namespace GodotTools.Build private TextEdit buildLog; private PopupMenu issuesListContextMenu; + private readonly object pendingBuildLogTextLock = new object(); + [NotNull] private string pendingBuildLogText = string.Empty; + [Signal] public event Action BuildStateChanged; public bool HasBuildExited { get; private set; } = false; @@ -240,16 +244,34 @@ namespace GodotTools.Build EmitSignal(nameof(BuildStateChanged)); } + private void UpdateBuildLogText() + { + lock (pendingBuildLogTextLock) + { + buildLog.Text += pendingBuildLogText; + pendingBuildLogText = string.Empty; + ScrollToLastNonEmptyLogLine(); + } + } + private void StdOutputReceived(string text) { - buildLog.Text += text + "\n"; - ScrollToLastNonEmptyLogLine(); + lock (pendingBuildLogTextLock) + { + if (pendingBuildLogText.Length == 0) + CallDeferred(nameof(UpdateBuildLogText)); + pendingBuildLogText += text + "\n"; + } } private void StdErrorReceived(string text) { - buildLog.Text += text + "\n"; - ScrollToLastNonEmptyLogLine(); + lock (pendingBuildLogTextLock) + { + if (pendingBuildLogText.Length == 0) + CallDeferred(nameof(UpdateBuildLogText)); + pendingBuildLogText += text + "\n"; + } } private void ScrollToLastNonEmptyLogLine() @@ -377,12 +399,14 @@ namespace GodotTools.Build BuildManager.BuildStarted += BuildStarted; BuildManager.BuildFinished += BuildFinished; // StdOutput/Error can be received from different threads, so we need to use CallDeferred - BuildManager.StdOutputReceived += line => CallDeferred(nameof(StdOutputReceived), line); - BuildManager.StdErrorReceived += line => CallDeferred(nameof(StdErrorReceived), line); + BuildManager.StdOutputReceived += StdOutputReceived; + BuildManager.StdErrorReceived += StdErrorReceived; } public void OnBeforeSerialize() { + // In case it didn't update yet. We don't want to have to serialize any pending output. + UpdateBuildLogText(); } public void OnAfterDeserialize() diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs index e18ed7f107..e9bb701562 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs @@ -173,6 +173,8 @@ namespace GodotTools.Export assemblies[projectDllName] = projectDllSrcPath; + string bclDir = DeterminePlatformBclDir(platform); + if (platform == OS.Platforms.Android) { string godotAndroidExtProfileDir = GetBclProfileDir("godot_android_ext"); @@ -183,8 +185,49 @@ namespace GodotTools.Export assemblies["Mono.Android"] = monoAndroidAssemblyPath; } + else if (platform == OS.Platforms.HTML5) + { + // Ideally these would be added automatically since they're referenced by the wasm BCL assemblies. + // However, at least in the case of 'WebAssembly.Net.Http' for some reason the BCL assemblies + // reference a different version even though the assembly is the same, for some weird reason. - string bclDir = DeterminePlatformBclDir(platform); + var wasmFrameworkAssemblies = new[] {"WebAssembly.Bindings", "WebAssembly.Net.WebSockets"}; + + foreach (string thisWasmFrameworkAssemblyName in wasmFrameworkAssemblies) + { + string thisWasmFrameworkAssemblyPath = Path.Combine(bclDir, thisWasmFrameworkAssemblyName + ".dll"); + if (!File.Exists(thisWasmFrameworkAssemblyPath)) + throw new FileNotFoundException($"Assembly not found: '{thisWasmFrameworkAssemblyName}'", thisWasmFrameworkAssemblyPath); + assemblies[thisWasmFrameworkAssemblyName] = thisWasmFrameworkAssemblyPath; + } + + // Assemblies that can have a different name in a newer version. Newer version must come first and it has priority. + (string newName, string oldName)[] wasmFrameworkAssembliesOneOf = new[] + { + ("System.Net.Http.WebAssemblyHttpHandler", "WebAssembly.Net.Http") + }; + + foreach (var thisWasmFrameworkAssemblyName in wasmFrameworkAssembliesOneOf) + { + string thisWasmFrameworkAssemblyPath = Path.Combine(bclDir, thisWasmFrameworkAssemblyName.newName + ".dll"); + if (File.Exists(thisWasmFrameworkAssemblyPath)) + { + assemblies[thisWasmFrameworkAssemblyName.newName] = thisWasmFrameworkAssemblyPath; + } + else + { + thisWasmFrameworkAssemblyPath = Path.Combine(bclDir, thisWasmFrameworkAssemblyName.oldName + ".dll"); + if (!File.Exists(thisWasmFrameworkAssemblyPath)) + { + throw new FileNotFoundException("Expected one of the following assemblies but none were found: " + + $"'{thisWasmFrameworkAssemblyName.newName}' / '{thisWasmFrameworkAssemblyName.oldName}'", + thisWasmFrameworkAssemblyPath); + } + + assemblies[thisWasmFrameworkAssemblyName.oldName] = thisWasmFrameworkAssemblyPath; + } + } + } var initialAssemblies = assemblies.Duplicate(); internal_GetExportedAssemblyDependencies(initialAssemblies, buildConfig, bclDir, assemblies); diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index ff3122a77f..59ce617990 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -97,7 +97,7 @@ #define C_METHOD_MANAGED_TO_SIGNAL C_NS_MONOMARSHAL "::signal_info_to_callable" #define C_METHOD_MANAGED_FROM_SIGNAL C_NS_MONOMARSHAL "::callable_to_signal_info" -#define BINDINGS_GENERATOR_VERSION UINT32_C(12) +#define BINDINGS_GENERATOR_VERSION UINT32_C(13) const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN("\t%0 %1_in = %1;\n"); @@ -181,11 +181,11 @@ static String snake_to_camel_case(const String &p_identifier, bool p_input_is_up String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype) { // Based on the version in EditorHelp - if (p_bbcode.empty()) { + if (p_bbcode.is_empty()) { return String(); } - DocData *doc = EditorHelp::get_doc_data(); + DocTools *doc = EditorHelp::get_doc_data(); String bbcode = p_bbcode; @@ -644,7 +644,7 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf } int BindingsGenerator::_determine_enum_prefix(const EnumInterface &p_ienum) { - CRASH_COND(p_ienum.constants.empty()); + CRASH_COND(p_ienum.constants.is_empty()); const ConstantInterface &front_iconstant = p_ienum.constants.front()->get(); Vector<String> front_parts = front_iconstant.name.split("_", /* p_allow_empty: */ true); @@ -819,7 +819,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) { p_output.append(";"); } - if (!global_constants.empty()) { + if (!global_constants.is_empty()) { p_output.append("\n"); } @@ -830,7 +830,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) { for (List<EnumInterface>::Element *E = global_enums.front(); E; E = E->next()) { const EnumInterface &ienum = E->get(); - CRASH_COND(ienum.constants.empty()); + CRASH_COND(ienum.constants.is_empty()); String enum_proxy_name = ienum.cname.operator String(); @@ -1283,7 +1283,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str for (const List<EnumInterface>::Element *E = itype.enums.front(); E; E = E->next()) { const EnumInterface &ienum = E->get(); - ERR_FAIL_COND_V(ienum.constants.empty(), ERR_BUG); + ERR_FAIL_COND_V(ienum.constants.is_empty(), ERR_BUG); output.append(MEMBER_BEGIN "public enum "); output.append(ienum.cname.operator String()); @@ -1661,14 +1661,14 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf cs_in_statements += def_arg; cs_in_statements += ";\n" INDENT3; - icall_params += arg_type->cs_in.empty() ? arg_in : sformat(arg_type->cs_in, arg_in); + icall_params += arg_type->cs_in.is_empty() ? arg_in : sformat(arg_type->cs_in, arg_in); // Apparently the name attribute must not include the @ String param_tag_name = iarg.name.begins_with("@") ? iarg.name.substr(1, iarg.name.length()) : iarg.name; default_args_doc.append(MEMBER_BEGIN "/// <param name=\"" + param_tag_name + "\">If the parameter is null, then the default value is " + def_arg + "</param>"); } else { - icall_params += arg_type->cs_in.empty() ? iarg.name : sformat(arg_type->cs_in, iarg.name); + icall_params += arg_type->cs_in.is_empty() ? iarg.name : sformat(arg_type->cs_in, iarg.name); } } @@ -1714,7 +1714,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf } if (p_imethod.is_deprecated) { - if (p_imethod.deprecation_message.empty()) { + if (p_imethod.deprecation_message.is_empty()) { WARN_PRINT("An empty deprecation message is discouraged. Method: '" + p_imethod.proxy_name + "'."); } @@ -1782,7 +1782,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf if (return_type->cname == name_cache.type_void) { p_output.append(im_call + "(" + icall_params + ");\n"); - } else if (return_type->cs_out.empty()) { + } else if (return_type->cs_out.is_empty()) { p_output.append("return " + im_call + "(" + icall_params + ");\n"); } else { p_output.append(sformat(return_type->cs_out, im_call, icall_params, return_type->cs_type, return_type->im_type_out)); @@ -1839,7 +1839,7 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf } if (p_isignal.is_deprecated) { - if (p_isignal.deprecation_message.empty()) { + if (p_isignal.deprecation_message.is_empty()) { WARN_PRINT("An empty deprecation message is discouraged. Signal: '" + p_isignal.proxy_name + "'."); } @@ -1999,12 +1999,12 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) { #define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \ { \ - output.append("\tmono_add_internal_call("); \ + output.append("\tGDMonoUtils::add_internal_call("); \ output.append("\"" BINDINGS_NAMESPACE "."); \ output.append(m_icall.editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); \ output.append("::"); \ output.append(m_icall.name); \ - output.append("\", (void*)"); \ + output.append("\", "); \ output.append(m_icall.name); \ output.append(");\n"); \ } @@ -2263,7 +2263,7 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte } if (!ret_void) { - if (return_type->c_out.empty()) { + if (return_type->c_out.is_empty()) { p_output.append("\treturn " C_LOCAL_RET ";\n"); } else if (return_type->ret_as_byref_arg) { p_output.append(sformat(return_type->c_out, return_type->c_type_out, C_LOCAL_RET, return_type->name, "arg_ret")); @@ -2585,7 +2585,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() { int argc = method_info.arguments.size(); - if (method_info.name.empty()) { + if (method_info.name.is_empty()) { continue; } @@ -3100,44 +3100,11 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { INSERT_INT_TYPE("sbyte", int8_t, int64_t); INSERT_INT_TYPE("short", int16_t, int64_t); INSERT_INT_TYPE("int", int32_t, int64_t); + INSERT_INT_TYPE("long", int64_t, int64_t); INSERT_INT_TYPE("byte", uint8_t, int64_t); INSERT_INT_TYPE("ushort", uint16_t, int64_t); INSERT_INT_TYPE("uint", uint32_t, int64_t); - - itype = TypeInterface::create_value_type(String("long")); - { - itype.c_out = "\treturn (%0)%1;\n"; - itype.c_in = "\t%0 %1_in = (%0)*%1;\n"; - itype.c_out = "\t*%3 = (%0)%1;\n"; - itype.c_type = "int64_t"; - itype.c_arg_in = "&%s_in"; - } - itype.c_type_in = "int64_t*"; - itype.c_type_out = "int64_t"; - itype.im_type_in = "ref " + itype.name; - itype.im_type_out = "out " + itype.name; - itype.cs_in = "ref %0"; - /* in cs_out, im_type_out (%3) includes the 'out ' part */ - itype.cs_out = "%0(%1, %3 argRet); return argRet;"; - itype.ret_as_byref_arg = true; - builtin_types.insert(itype.cname, itype); - - itype = TypeInterface::create_value_type(String("ulong")); - { - itype.c_in = "\t%0 %1_in = (%0)*%1;\n"; - itype.c_out = "\t*%3 = (%0)%1;\n"; - itype.c_type = "int64_t"; - itype.c_arg_in = "&%s_in"; - } - itype.c_type_in = "uint64_t*"; - itype.c_type_out = "uint64_t"; - itype.im_type_in = "ref " + itype.name; - itype.im_type_out = "out " + itype.name; - itype.cs_in = "ref %0"; - /* in cs_out, im_type_out (%3) includes the 'out ' part */ - itype.cs_out = "%0(%1, %3 argRet); return argRet;"; - itype.ret_as_byref_arg = true; - builtin_types.insert(itype.cname, itype); + INSERT_INT_TYPE("ulong", uint64_t, int64_t); } // Floating point types @@ -3149,20 +3116,16 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.proxy_name = "float"; { // The expected type for 'float' in ptrcall is 'double' - itype.c_in = "\t%0 %1_in = (%0)*%1;\n"; - itype.c_out = "\t*%3 = (%0)%1;\n"; + itype.c_in = "\t%0 %1_in = (%0)%1;\n"; + itype.c_out = "\treturn (%0)%1;\n"; itype.c_type = "double"; - itype.c_type_in = "float*"; + itype.c_type_in = "float"; itype.c_type_out = "float"; itype.c_arg_in = "&%s_in"; } itype.cs_type = itype.proxy_name; - itype.im_type_in = "ref " + itype.proxy_name; - itype.im_type_out = "out " + itype.proxy_name; - itype.cs_in = "ref %0"; - /* in cs_out, im_type_out (%3) includes the 'out ' part */ - itype.cs_out = "%0(%1, %3 argRet); return argRet;"; - itype.ret_as_byref_arg = true; + itype.im_type_in = itype.proxy_name; + itype.im_type_out = itype.proxy_name; builtin_types.insert(itype.cname, itype); // double @@ -3171,20 +3134,14 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.cname = itype.name; itype.proxy_name = "double"; { - itype.c_in = "\t%0 %1_in = (%0)*%1;\n"; - itype.c_out = "\t*%3 = (%0)%1;\n"; itype.c_type = "double"; - itype.c_type_in = "double*"; + itype.c_type_in = "double"; itype.c_type_out = "double"; - itype.c_arg_in = "&%s_in"; + itype.c_arg_in = "&%s"; } itype.cs_type = itype.proxy_name; - itype.im_type_in = "ref " + itype.proxy_name; - itype.im_type_out = "out " + itype.proxy_name; - itype.cs_in = "ref %0"; - /* in cs_out, im_type_out (%3) includes the 'out ' part */ - itype.cs_out = "%0(%1, %3 argRet); return argRet;"; - itype.ret_as_byref_arg = true; + itype.im_type_in = itype.proxy_name; + itype.im_type_out = itype.proxy_name; builtin_types.insert(itype.cname, itype); } diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h index eeab518954..b18dfb0ec4 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,9 +31,10 @@ #ifndef BINDINGS_GENERATOR_H #define BINDINGS_GENERATOR_H +#include "core/doc_data.h" #include "core/object/class_db.h" #include "core/string/string_builder.h" -#include "editor/doc_data.h" +#include "editor/doc_tools.h" #include "editor/editor_help.h" #if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED) @@ -44,7 +45,7 @@ class BindingsGenerator { struct ConstantInterface { String name; String proxy_name; - int value; + int value = 0; const DocData::ConstantDoc *const_doc; ConstantInterface() {} @@ -74,7 +75,7 @@ class BindingsGenerator { struct PropertyInterface { StringName cname; String proxy_name; - int index; + int index = 0; StringName setter; StringName getter; @@ -479,7 +480,7 @@ class BindingsGenerator { String im_type_out; // Return type for the C# method declaration. Also used as companion of [unique_siq] String im_sig; // Signature for the C# method declaration String unique_sig; // Unique signature to avoid duplicates in containers - bool editor_only; + bool editor_only = false; InternalCall() {} diff --git a/modules/mono/editor/code_completion.cpp b/modules/mono/editor/code_completion.cpp index 2d37b1306c..bbfba83e6f 100644 --- a/modules/mono/editor/code_completion.cpp +++ b/modules/mono/editor/code_completion.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -150,7 +150,7 @@ PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_scr List<String> directories; directories.push_back(dir_access->get_current_dir()); - while (!directories.empty()) { + while (!directories.is_empty()) { dir_access->change_dir(directories.back()->get()); directories.pop_back(); diff --git a/modules/mono/editor/code_completion.h b/modules/mono/editor/code_completion.h index e38768612b..7f7521672b 100644 --- a/modules/mono/editor/code_completion.h +++ b/modules/mono/editor/code_completion.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp index 68fc372959..667e4a3879 100644 --- a/modules/mono/editor/editor_internal_calls.cpp +++ b/modules/mono/editor/editor_internal_calls.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -47,7 +47,6 @@ #include "../godotsharp_dirs.h" #include "../mono_gd/gd_mono_marshal.h" #include "../utils/osx_utils.h" -#include "bindings_generator.h" #include "code_completion.h" #include "godotsharp_export.h" #include "script_class_parser.h" @@ -173,35 +172,6 @@ MonoBoolean godot_icall_EditorProgress_Step(MonoString *p_task, MonoString *p_st return EditorNode::progress_task_step(task, state, p_step, (bool)p_force_refresh); } -BindingsGenerator *godot_icall_BindingsGenerator_Ctor() { - return memnew(BindingsGenerator); -} - -void godot_icall_BindingsGenerator_Dtor(BindingsGenerator *p_handle) { - memdelete(p_handle); -} - -MonoBoolean godot_icall_BindingsGenerator_LogPrintEnabled(BindingsGenerator *p_handle) { - return p_handle->is_log_print_enabled(); -} - -void godot_icall_BindingsGenerator_SetLogPrintEnabled(BindingsGenerator p_handle, MonoBoolean p_enabled) { - p_handle.set_log_print_enabled(p_enabled); -} - -int32_t godot_icall_BindingsGenerator_GenerateCsApi(BindingsGenerator *p_handle, MonoString *p_output_dir) { - String output_dir = GDMonoMarshal::mono_string_to_godot(p_output_dir); - return p_handle->generate_cs_api(output_dir); -} - -uint32_t godot_icall_BindingsGenerator_Version() { - return BindingsGenerator::get_version(); -} - -uint32_t godot_icall_BindingsGenerator_CsGlueVersion() { - return CS_GLUE_VERSION; -} - int32_t godot_icall_ScriptClassParser_ParseFile(MonoString *p_filepath, MonoObject *p_classes, MonoString **r_error_str) { *r_error_str = nullptr; @@ -225,7 +195,7 @@ int32_t godot_icall_ScriptClassParser_ParseFile(MonoString *p_filepath, MonoObje } } else { String error_str = scp.get_error(); - if (!error_str.empty()) { + if (!error_str.is_empty()) { *r_error_str = GDMonoMarshal::mono_string_from_godot(error_str); } } @@ -400,75 +370,66 @@ MonoBoolean godot_icall_Utils_OS_UnixFileHasExecutableAccess(MonoString *p_file_ void register_editor_internal_calls() { // GodotSharpDirs - mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResDataDir", (void *)godot_icall_GodotSharpDirs_ResDataDir); - mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResMetadataDir", (void *)godot_icall_GodotSharpDirs_ResMetadataDir); - mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResAssembliesBaseDir", (void *)godot_icall_GodotSharpDirs_ResAssembliesBaseDir); - mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResAssembliesDir", (void *)godot_icall_GodotSharpDirs_ResAssembliesDir); - mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResConfigDir", (void *)godot_icall_GodotSharpDirs_ResConfigDir); - mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempDir", (void *)godot_icall_GodotSharpDirs_ResTempDir); - mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempAssembliesBaseDir", (void *)godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir); - mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempAssembliesDir", (void *)godot_icall_GodotSharpDirs_ResTempAssembliesDir); - mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoUserDir", (void *)godot_icall_GodotSharpDirs_MonoUserDir); - mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoLogsDir", (void *)godot_icall_GodotSharpDirs_MonoLogsDir); - mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoSolutionsDir", (void *)godot_icall_GodotSharpDirs_MonoSolutionsDir); - mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_BuildLogsDirs", (void *)godot_icall_GodotSharpDirs_BuildLogsDirs); - mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectSlnPath", (void *)godot_icall_GodotSharpDirs_ProjectSlnPath); - mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectCsProjPath", (void *)godot_icall_GodotSharpDirs_ProjectCsProjPath); - mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorToolsDir", (void *)godot_icall_GodotSharpDirs_DataEditorToolsDir); - mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorPrebuiltApiDir", (void *)godot_icall_GodotSharpDirs_DataEditorPrebuiltApiDir); - mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoEtcDir", (void *)godot_icall_GodotSharpDirs_DataMonoEtcDir); - mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoLibDir", (void *)godot_icall_GodotSharpDirs_DataMonoLibDir); - mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoBinDir", (void *)godot_icall_GodotSharpDirs_DataMonoBinDir); + GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResDataDir", godot_icall_GodotSharpDirs_ResDataDir); + GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResMetadataDir", godot_icall_GodotSharpDirs_ResMetadataDir); + GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResAssembliesBaseDir", godot_icall_GodotSharpDirs_ResAssembliesBaseDir); + GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResAssembliesDir", godot_icall_GodotSharpDirs_ResAssembliesDir); + GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResConfigDir", godot_icall_GodotSharpDirs_ResConfigDir); + GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempDir", godot_icall_GodotSharpDirs_ResTempDir); + GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempAssembliesBaseDir", godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir); + GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempAssembliesDir", godot_icall_GodotSharpDirs_ResTempAssembliesDir); + GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoUserDir", godot_icall_GodotSharpDirs_MonoUserDir); + GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoLogsDir", godot_icall_GodotSharpDirs_MonoLogsDir); + GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoSolutionsDir", godot_icall_GodotSharpDirs_MonoSolutionsDir); + GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_BuildLogsDirs", godot_icall_GodotSharpDirs_BuildLogsDirs); + GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectSlnPath", godot_icall_GodotSharpDirs_ProjectSlnPath); + GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectCsProjPath", godot_icall_GodotSharpDirs_ProjectCsProjPath); + GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorToolsDir", godot_icall_GodotSharpDirs_DataEditorToolsDir); + GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorPrebuiltApiDir", godot_icall_GodotSharpDirs_DataEditorPrebuiltApiDir); + GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoEtcDir", godot_icall_GodotSharpDirs_DataMonoEtcDir); + GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoLibDir", godot_icall_GodotSharpDirs_DataMonoLibDir); + GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoBinDir", godot_icall_GodotSharpDirs_DataMonoBinDir); // EditorProgress - mono_add_internal_call("GodotTools.Internals.EditorProgress::internal_Create", (void *)godot_icall_EditorProgress_Create); - mono_add_internal_call("GodotTools.Internals.EditorProgress::internal_Dispose", (void *)godot_icall_EditorProgress_Dispose); - mono_add_internal_call("GodotTools.Internals.EditorProgress::internal_Step", (void *)godot_icall_EditorProgress_Step); - - // BiningsGenerator - mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_Ctor", (void *)godot_icall_BindingsGenerator_Ctor); - mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_Dtor", (void *)godot_icall_BindingsGenerator_Dtor); - mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_LogPrintEnabled", (void *)godot_icall_BindingsGenerator_LogPrintEnabled); - mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_SetLogPrintEnabled", (void *)godot_icall_BindingsGenerator_SetLogPrintEnabled); - mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_GenerateCsApi", (void *)godot_icall_BindingsGenerator_GenerateCsApi); - mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_Version", (void *)godot_icall_BindingsGenerator_Version); - mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_CsGlueVersion", (void *)godot_icall_BindingsGenerator_CsGlueVersion); + GDMonoUtils::add_internal_call("GodotTools.Internals.EditorProgress::internal_Create", godot_icall_EditorProgress_Create); + GDMonoUtils::add_internal_call("GodotTools.Internals.EditorProgress::internal_Dispose", godot_icall_EditorProgress_Dispose); + GDMonoUtils::add_internal_call("GodotTools.Internals.EditorProgress::internal_Step", godot_icall_EditorProgress_Step); // ScriptClassParser - mono_add_internal_call("GodotTools.Internals.ScriptClassParser::internal_ParseFile", (void *)godot_icall_ScriptClassParser_ParseFile); + GDMonoUtils::add_internal_call("GodotTools.Internals.ScriptClassParser::internal_ParseFile", godot_icall_ScriptClassParser_ParseFile); // ExportPlugin - mono_add_internal_call("GodotTools.Export.ExportPlugin::internal_GetExportedAssemblyDependencies", (void *)godot_icall_ExportPlugin_GetExportedAssemblyDependencies); + GDMonoUtils::add_internal_call("GodotTools.Export.ExportPlugin::internal_GetExportedAssemblyDependencies", godot_icall_ExportPlugin_GetExportedAssemblyDependencies); // Internals - mono_add_internal_call("GodotTools.Internals.Internal::internal_UpdateApiAssembliesFromPrebuilt", (void *)godot_icall_Internal_UpdateApiAssembliesFromPrebuilt); - mono_add_internal_call("GodotTools.Internals.Internal::internal_FullTemplatesDir", (void *)godot_icall_Internal_FullTemplatesDir); - mono_add_internal_call("GodotTools.Internals.Internal::internal_SimplifyGodotPath", (void *)godot_icall_Internal_SimplifyGodotPath); - mono_add_internal_call("GodotTools.Internals.Internal::internal_IsOsxAppBundleInstalled", (void *)godot_icall_Internal_IsOsxAppBundleInstalled); - mono_add_internal_call("GodotTools.Internals.Internal::internal_GodotIs32Bits", (void *)godot_icall_Internal_GodotIs32Bits); - mono_add_internal_call("GodotTools.Internals.Internal::internal_GodotIsRealTDouble", (void *)godot_icall_Internal_GodotIsRealTDouble); - mono_add_internal_call("GodotTools.Internals.Internal::internal_GodotMainIteration", (void *)godot_icall_Internal_GodotMainIteration); - mono_add_internal_call("GodotTools.Internals.Internal::internal_GetCoreApiHash", (void *)godot_icall_Internal_GetCoreApiHash); - mono_add_internal_call("GodotTools.Internals.Internal::internal_GetEditorApiHash", (void *)godot_icall_Internal_GetEditorApiHash); - mono_add_internal_call("GodotTools.Internals.Internal::internal_IsAssembliesReloadingNeeded", (void *)godot_icall_Internal_IsAssembliesReloadingNeeded); - mono_add_internal_call("GodotTools.Internals.Internal::internal_ReloadAssemblies", (void *)godot_icall_Internal_ReloadAssemblies); - mono_add_internal_call("GodotTools.Internals.Internal::internal_EditorDebuggerNodeReloadScripts", (void *)godot_icall_Internal_EditorDebuggerNodeReloadScripts); - mono_add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorEdit", (void *)godot_icall_Internal_ScriptEditorEdit); - mono_add_internal_call("GodotTools.Internals.Internal::internal_EditorNodeShowScriptScreen", (void *)godot_icall_Internal_EditorNodeShowScriptScreen); - mono_add_internal_call("GodotTools.Internals.Internal::internal_GetScriptsMetadataOrNothing", (void *)godot_icall_Internal_GetScriptsMetadataOrNothing); - mono_add_internal_call("GodotTools.Internals.Internal::internal_MonoWindowsInstallRoot", (void *)godot_icall_Internal_MonoWindowsInstallRoot); - mono_add_internal_call("GodotTools.Internals.Internal::internal_EditorRunPlay", (void *)godot_icall_Internal_EditorRunPlay); - mono_add_internal_call("GodotTools.Internals.Internal::internal_EditorRunStop", (void *)godot_icall_Internal_EditorRunStop); - mono_add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorDebugger_ReloadScripts", (void *)godot_icall_Internal_ScriptEditorDebugger_ReloadScripts); - mono_add_internal_call("GodotTools.Internals.Internal::internal_CodeCompletionRequest", (void *)godot_icall_Internal_CodeCompletionRequest); + GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_UpdateApiAssembliesFromPrebuilt", godot_icall_Internal_UpdateApiAssembliesFromPrebuilt); + GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_FullTemplatesDir", godot_icall_Internal_FullTemplatesDir); + GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_SimplifyGodotPath", godot_icall_Internal_SimplifyGodotPath); + GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_IsOsxAppBundleInstalled", godot_icall_Internal_IsOsxAppBundleInstalled); + GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotIs32Bits", godot_icall_Internal_GodotIs32Bits); + GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotIsRealTDouble", godot_icall_Internal_GodotIsRealTDouble); + GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotMainIteration", godot_icall_Internal_GodotMainIteration); + GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GetCoreApiHash", godot_icall_Internal_GetCoreApiHash); + GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GetEditorApiHash", godot_icall_Internal_GetEditorApiHash); + GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_IsAssembliesReloadingNeeded", godot_icall_Internal_IsAssembliesReloadingNeeded); + GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_ReloadAssemblies", godot_icall_Internal_ReloadAssemblies); + GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_EditorDebuggerNodeReloadScripts", godot_icall_Internal_EditorDebuggerNodeReloadScripts); + GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorEdit", godot_icall_Internal_ScriptEditorEdit); + GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_EditorNodeShowScriptScreen", godot_icall_Internal_EditorNodeShowScriptScreen); + GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GetScriptsMetadataOrNothing", godot_icall_Internal_GetScriptsMetadataOrNothing); + GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_MonoWindowsInstallRoot", godot_icall_Internal_MonoWindowsInstallRoot); + GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_EditorRunPlay", godot_icall_Internal_EditorRunPlay); + GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_EditorRunStop", godot_icall_Internal_EditorRunStop); + GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorDebugger_ReloadScripts", godot_icall_Internal_ScriptEditorDebugger_ReloadScripts); + GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_CodeCompletionRequest", godot_icall_Internal_CodeCompletionRequest); // Globals - mono_add_internal_call("GodotTools.Internals.Globals::internal_EditorScale", (void *)godot_icall_Globals_EditorScale); - mono_add_internal_call("GodotTools.Internals.Globals::internal_GlobalDef", (void *)godot_icall_Globals_GlobalDef); - mono_add_internal_call("GodotTools.Internals.Globals::internal_EditorDef", (void *)godot_icall_Globals_EditorDef); - mono_add_internal_call("GodotTools.Internals.Globals::internal_TTR", (void *)godot_icall_Globals_TTR); + GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_EditorScale", godot_icall_Globals_EditorScale); + GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_GlobalDef", godot_icall_Globals_GlobalDef); + GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_EditorDef", godot_icall_Globals_EditorDef); + GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_TTR", godot_icall_Globals_TTR); // Utils.OS - mono_add_internal_call("GodotTools.Utils.OS::GetPlatformName", (void *)godot_icall_Utils_OS_GetPlatformName); - mono_add_internal_call("GodotTools.Utils.OS::UnixFileHasExecutableAccess", (void *)godot_icall_Utils_OS_UnixFileHasExecutableAccess); + GDMonoUtils::add_internal_call("GodotTools.Utils.OS::GetPlatformName", godot_icall_Utils_OS_GetPlatformName); + GDMonoUtils::add_internal_call("GodotTools.Utils.OS::UnixFileHasExecutableAccess", godot_icall_Utils_OS_UnixFileHasExecutableAccess); } diff --git a/modules/mono/editor/editor_internal_calls.h b/modules/mono/editor/editor_internal_calls.h index ef4e639161..24080cd867 100644 --- a/modules/mono/editor/editor_internal_calls.h +++ b/modules/mono/editor/editor_internal_calls.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp index 1a0d5743ae..4b858c0e82 100644 --- a/modules/mono/editor/godotsharp_export.cpp +++ b/modules/mono/editor/godotsharp_export.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -55,10 +55,10 @@ MonoAssemblyName *new_mono_assembly_name() { struct AssemblyRefInfo { String name; - uint16_t major; - uint16_t minor; - uint16_t build; - uint16_t revision; + uint16_t major = 0; + uint16_t minor = 0; + uint16_t build = 0; + uint16_t revision = 0; }; AssemblyRefInfo get_assemblyref_name(MonoImage *p_image, int index) { diff --git a/modules/mono/editor/godotsharp_export.h b/modules/mono/editor/godotsharp_export.h index 586d4e5a0c..0e9d689618 100644 --- a/modules/mono/editor/godotsharp_export.h +++ b/modules/mono/editor/godotsharp_export.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/editor/script_class_parser.cpp b/modules/mono/editor/script_class_parser.cpp index 70940c279e..8f9a31a5b9 100644 --- a/modules/mono/editor/script_class_parser.cpp +++ b/modules/mono/editor/script_class_parser.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/editor/script_class_parser.h b/modules/mono/editor/script_class_parser.h index 3c55fa07a7..75a46bb4e5 100644 --- a/modules/mono/editor/script_class_parser.h +++ b/modules/mono/editor/script_class_parser.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -45,22 +45,22 @@ public: }; String name; - Type type; + Type type = NAMESPACE_DECL; }; struct ClassDecl { String name; String namespace_; Vector<String> base; - bool nested; + bool nested = false; }; private: String code; - int idx; - int line; + int idx = 0; + int line = 0; String error_str; - bool error; + bool error = false; Variant value; Vector<ClassDecl> classes; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs index 6eecc262d6..c3f372d415 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs @@ -618,10 +618,10 @@ namespace Godot /// This can also be used to round a floating point /// number to an arbitrary number of decimals. /// </summary> - /// <param name="s">The value to stepify.</param> + /// <param name="s">The value to snap.</param> /// <param name="step">The step size to snap to.</param> /// <returns></returns> - public static real_t Stepify(real_t s, real_t step) + public static real_t Snapped(real_t s, real_t step) { if (step != 0f) { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quat.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quat.cs index b33490f9cb..bd3bcb0c58 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quat.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quat.cs @@ -120,13 +120,13 @@ namespace Godot /// <param name="b">The destination quaternion.</param> /// <param name="preA">A quaternion before this quaternion.</param> /// <param name="postB">A quaternion after `b`.</param> - /// <param name="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> + /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> /// <returns>The interpolated quaternion.</returns> - public Quat CubicSlerp(Quat b, Quat preA, Quat postB, real_t t) + public Quat CubicSlerp(Quat b, Quat preA, Quat postB, real_t weight) { - real_t t2 = (1.0f - t) * t * 2f; - Quat sp = Slerp(b, t); - Quat sq = preA.Slerpni(postB, t); + real_t t2 = (1.0f - weight) * weight * 2f; + Quat sp = Slerp(b, weight); + Quat sq = preA.Slerpni(postB, weight); return sp.Slerpni(sq, t2); } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs index f7703c77cc..868c3536fe 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs @@ -52,7 +52,7 @@ namespace Godot } /// <summary> - /// The area of this rect. + /// The area of this Rect2. /// </summary> /// <value>Equivalent to <see cref="GetArea()"/>.</value> public real_t Area @@ -64,7 +64,7 @@ namespace Godot /// Returns a Rect2 with equivalent position and size, modified so that /// the top-left corner is the origin and width and height are positive. /// </summary> - /// <returns>The modified rect.</returns> + /// <returns>The modified Rect2.</returns> public Rect2 Abs() { Vector2 end = End; @@ -74,10 +74,11 @@ namespace Godot /// <summary> /// Returns the intersection of this Rect2 and `b`. + /// If the rectangles do not intersect, an empty Rect2 is returned. /// </summary> - /// <param name="b">The other rect.</param> - /// <returns>The clipped rect.</returns> - public Rect2 Clip(Rect2 b) + /// <param name="b">The other Rect2.</param> + /// <returns>The intersection of this Rect2 and `b`, or an empty Rect2 if they do not intersect.</returns> + public Rect2 Intersection(Rect2 b) { var newRect = b; @@ -101,8 +102,8 @@ namespace Godot /// <summary> /// Returns true if this Rect2 completely encloses another one. /// </summary> - /// <param name="b">The other rect that may be enclosed.</param> - /// <returns>A bool for whether or not this rect encloses `b`.</returns> + /// <param name="b">The other Rect2 that may be enclosed.</param> + /// <returns>A bool for whether or not this Rect2 encloses `b`.</returns> public bool Encloses(Rect2 b) { return b._position.x >= _position.x && b._position.y >= _position.y && @@ -114,7 +115,7 @@ namespace Godot /// Returns this Rect2 expanded to include a given point. /// </summary> /// <param name="to">The point to include.</param> - /// <returns>The expanded rect.</returns> + /// <returns>The expanded Rect2.</returns> public Rect2 Expand(Vector2 to) { var expanded = this; @@ -156,10 +157,10 @@ namespace Godot } /// <summary> - /// Returns a copy of the Rect2 grown a given amount of units towards all the sides. + /// Returns a copy of the Rect2 grown by the specified amount on all sides. /// </summary> /// <param name="by">The amount to grow by.</param> - /// <returns>The grown rect.</returns> + /// <returns>The grown Rect2.</returns> public Rect2 Grow(real_t by) { var g = this; @@ -173,13 +174,13 @@ namespace Godot } /// <summary> - /// Returns a copy of the Rect2 grown a given amount of units towards each direction individually. + /// Returns a copy of the Rect2 grown by the specified amount on each side individually. /// </summary> - /// <param name="left">The amount to grow by on the left.</param> - /// <param name="top">The amount to grow by on the top.</param> - /// <param name="right">The amount to grow by on the right.</param> - /// <param name="bottom">The amount to grow by on the bottom.</param> - /// <returns>The grown rect.</returns> + /// <param name="left">The amount to grow by on the left side.</param> + /// <param name="top">The amount to grow by on the top side.</param> + /// <param name="right">The amount to grow by on the right side.</param> + /// <param name="bottom">The amount to grow by on the bottom side.</param> + /// <returns>The grown Rect2.</returns> public Rect2 GrowIndividual(real_t left, real_t top, real_t right, real_t bottom) { var g = this; @@ -193,19 +194,19 @@ namespace Godot } /// <summary> - /// Returns a copy of the Rect2 grown a given amount of units towards the <see cref="Margin"/> direction. + /// Returns a copy of the Rect2 grown by the specified amount on the specified Side. /// </summary> - /// <param name="margin">The direction to grow in.</param> + /// <param name="side">The side to grow.</param> /// <param name="by">The amount to grow by.</param> - /// <returns>The grown rect.</returns> - public Rect2 GrowMargin(Margin margin, real_t by) + /// <returns>The grown Rect2.</returns> + public Rect2 GrowSide(Side side, real_t by) { var g = this; - g = g.GrowIndividual(Margin.Left == margin ? by : 0, - Margin.Top == margin ? by : 0, - Margin.Right == margin ? by : 0, - Margin.Bottom == margin ? by : 0); + g = g.GrowIndividual(Side.Left == side ? by : 0, + Side.Top == side ? by : 0, + Side.Right == side ? by : 0, + Side.Bottom == side ? by : 0); return g; } @@ -213,7 +214,7 @@ namespace Godot /// <summary> /// Returns true if the Rect2 is flat or empty, or false otherwise. /// </summary> - /// <returns>A bool for whether or not the rect has area.</returns> + /// <returns>A bool for whether or not the Rect2 has area.</returns> public bool HasNoArea() { return _size.x <= 0 || _size.y <= 0; @@ -223,7 +224,7 @@ namespace Godot /// Returns true if the Rect2 contains a point, or false otherwise. /// </summary> /// <param name="point">The point to check.</param> - /// <returns>A bool for whether or not the rect contains `point`.</returns> + /// <returns>A bool for whether or not the Rect2 contains `point`.</returns> public bool HasPoint(Vector2 point) { if (point.x < _position.x) @@ -246,7 +247,7 @@ namespace Godot /// If `includeBorders` is true, they will also be considered overlapping /// if their borders touch, even without intersection. /// </summary> - /// <param name="b">The other rect to check for intersections with.</param> + /// <param name="b">The other Rect2 to check for intersections with.</param> /// <param name="includeBorders">Whether or not to consider borders.</param> /// <returns>A bool for whether or not they are intersecting.</returns> public bool Intersects(Rect2 b, bool includeBorders = false) @@ -296,8 +297,8 @@ namespace Godot /// <summary> /// Returns a larger Rect2 that contains this Rect2 and `b`. /// </summary> - /// <param name="b">The other rect.</param> - /// <returns>The merged rect.</returns> + /// <param name="b">The other Rect2.</param> + /// <returns>The merged Rect2.</returns> public Rect2 Merge(Rect2 b) { Rect2 newRect; @@ -387,11 +388,11 @@ namespace Godot } /// <summary> - /// Returns true if this rect and `other` are approximately equal, by running + /// Returns true if this Rect2 and `other` are approximately equal, by running /// <see cref="Vector2.IsEqualApprox(Vector2)"/> on each component. /// </summary> - /// <param name="other">The other rect to compare.</param> - /// <returns>Whether or not the rects are approximately equal.</returns> + /// <param name="other">The other Rect2 to compare.</param> + /// <returns>Whether or not the Rect2s are approximately equal.</returns> public bool IsEqualApprox(Rect2 other) { return _position.IsEqualApprox(other._position) && _size.IsEqualApprox(other.Size); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs index 8f71c00d76..c27af74866 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs @@ -47,7 +47,7 @@ namespace Godot } /// <summary> - /// The area of this rect. + /// The area of this Rect2i. /// </summary> /// <value>Equivalent to <see cref="GetArea()"/>.</value> public int Area @@ -59,7 +59,7 @@ namespace Godot /// Returns a Rect2i with equivalent position and size, modified so that /// the top-left corner is the origin and width and height are positive. /// </summary> - /// <returns>The modified rect.</returns> + /// <returns>The modified Rect2i.</returns> public Rect2i Abs() { Vector2i end = End; @@ -69,10 +69,11 @@ namespace Godot /// <summary> /// Returns the intersection of this Rect2i and `b`. + /// If the rectangles do not intersect, an empty Rect2i is returned. /// </summary> - /// <param name="b">The other rect.</param> - /// <returns>The clipped rect.</returns> - public Rect2i Clip(Rect2i b) + /// <param name="b">The other Rect2i.</param> + /// <returns>The intersection of this Rect2i and `b`, or an empty Rect2i if they do not intersect.</returns> + public Rect2i Intersection(Rect2i b) { var newRect = b; @@ -96,8 +97,8 @@ namespace Godot /// <summary> /// Returns true if this Rect2i completely encloses another one. /// </summary> - /// <param name="b">The other rect that may be enclosed.</param> - /// <returns>A bool for whether or not this rect encloses `b`.</returns> + /// <param name="b">The other Rect2i that may be enclosed.</param> + /// <returns>A bool for whether or not this Rect2i encloses `b`.</returns> public bool Encloses(Rect2i b) { return b._position.x >= _position.x && b._position.y >= _position.y && @@ -109,7 +110,7 @@ namespace Godot /// Returns this Rect2i expanded to include a given point. /// </summary> /// <param name="to">The point to include.</param> - /// <returns>The expanded rect.</returns> + /// <returns>The expanded Rect2i.</returns> public Rect2i Expand(Vector2i to) { var expanded = this; @@ -151,10 +152,10 @@ namespace Godot } /// <summary> - /// Returns a copy of the Rect2i grown a given amount of units towards all the sides. + /// Returns a copy of the Rect2i grown by the specified amount on all sides. /// </summary> /// <param name="by">The amount to grow by.</param> - /// <returns>The grown rect.</returns> + /// <returns>The grown Rect2i.</returns> public Rect2i Grow(int by) { var g = this; @@ -168,13 +169,13 @@ namespace Godot } /// <summary> - /// Returns a copy of the Rect2i grown a given amount of units towards each direction individually. + /// Returns a copy of the Rect2i grown by the specified amount on each side individually. /// </summary> - /// <param name="left">The amount to grow by on the left.</param> - /// <param name="top">The amount to grow by on the top.</param> - /// <param name="right">The amount to grow by on the right.</param> - /// <param name="bottom">The amount to grow by on the bottom.</param> - /// <returns>The grown rect.</returns> + /// <param name="left">The amount to grow by on the left side.</param> + /// <param name="top">The amount to grow by on the top side.</param> + /// <param name="right">The amount to grow by on the right side.</param> + /// <param name="bottom">The amount to grow by on the bottom side.</param> + /// <returns>The grown Rect2i.</returns> public Rect2i GrowIndividual(int left, int top, int right, int bottom) { var g = this; @@ -188,37 +189,37 @@ namespace Godot } /// <summary> - /// Returns a copy of the Rect2i grown a given amount of units towards the <see cref="Margin"/> direction. + /// Returns a copy of the Rect2i grown by the specified amount on the specified Side. /// </summary> - /// <param name="margin">The direction to grow in.</param> + /// <param name="side">The side to grow.</param> /// <param name="by">The amount to grow by.</param> - /// <returns>The grown rect.</returns> - public Rect2i GrowMargin(Margin margin, int by) + /// <returns>The grown Rect2i.</returns> + public Rect2i GrowSide(Side side, int by) { var g = this; - g = g.GrowIndividual(Margin.Left == margin ? by : 0, - Margin.Top == margin ? by : 0, - Margin.Right == margin ? by : 0, - Margin.Bottom == margin ? by : 0); + g = g.GrowIndividual(Side.Left == side ? by : 0, + Side.Top == side ? by : 0, + Side.Right == side ? by : 0, + Side.Bottom == side ? by : 0); return g; } /// <summary> - /// Returns true if the Rect2 is flat or empty, or false otherwise. + /// Returns true if the Rect2i is flat or empty, or false otherwise. /// </summary> - /// <returns>A bool for whether or not the rect has area.</returns> + /// <returns>A bool for whether or not the Rect2i has area.</returns> public bool HasNoArea() { return _size.x <= 0 || _size.y <= 0; } /// <summary> - /// Returns true if the Rect2 contains a point, or false otherwise. + /// Returns true if the Rect2i contains a point, or false otherwise. /// </summary> /// <param name="point">The point to check.</param> - /// <returns>A bool for whether or not the rect contains `point`.</returns> + /// <returns>A bool for whether or not the Rect2i contains `point`.</returns> public bool HasPoint(Vector2i point) { if (point.x < _position.x) @@ -241,7 +242,7 @@ namespace Godot /// If `includeBorders` is true, they will also be considered overlapping /// if their borders touch, even without intersection. /// </summary> - /// <param name="b">The other rect to check for intersections with.</param> + /// <param name="b">The other Rect2i to check for intersections with.</param> /// <param name="includeBorders">Whether or not to consider borders.</param> /// <returns>A bool for whether or not they are intersecting.</returns> public bool Intersects(Rect2i b, bool includeBorders = false) @@ -273,10 +274,10 @@ namespace Godot } /// <summary> - /// Returns a larger Rect2i that contains this Rect2 and `b`. + /// Returns a larger Rect2i that contains this Rect2i and `b`. /// </summary> - /// <param name="b">The other rect.</param> - /// <returns>The merged rect.</returns> + /// <param name="b">The other Rect2i.</param> + /// <returns>The merged Rect2i.</returns> public Rect2i Merge(Rect2i b) { Rect2i newRect; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs index d536b14eac..6279ea1ace 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs @@ -194,15 +194,16 @@ namespace Godot /// <param name="b">The destination vector.</param> /// <param name="preA">A vector before this vector.</param> /// <param name="postB">A vector after `b`.</param> - /// <param name="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> + /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> /// <returns>The interpolated vector.</returns> - public Vector2 CubicInterpolate(Vector2 b, Vector2 preA, Vector2 postB, real_t t) + public Vector2 CubicInterpolate(Vector2 b, Vector2 preA, Vector2 postB, real_t weight) { Vector2 p0 = preA; Vector2 p1 = this; Vector2 p2 = b; Vector2 p3 = postB; + real_t t = weight; real_t t2 = t * t; real_t t3 = t2 * t; @@ -510,7 +511,7 @@ namespace Godot /// <returns>The snapped vector.</returns> public Vector2 Snapped(Vector2 step) { - return new Vector2(Mathf.Stepify(x, step.x), Mathf.Stepify(y, step.y)); + return new Vector2(Mathf.Snapped(x, step.x), Mathf.Snapped(y, step.y)); } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs index 4a4a2a43cd..42dbdf25c3 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs @@ -161,15 +161,16 @@ namespace Godot /// <param name="b">The destination vector.</param> /// <param name="preA">A vector before this vector.</param> /// <param name="postB">A vector after `b`.</param> - /// <param name="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> + /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> /// <returns>The interpolated vector.</returns> - public Vector3 CubicInterpolate(Vector3 b, Vector3 preA, Vector3 postB, real_t t) + public Vector3 CubicInterpolate(Vector3 b, Vector3 preA, Vector3 postB, real_t weight) { Vector3 p0 = preA; Vector3 p1 = this; Vector3 p2 = b; Vector3 p3 = postB; + real_t t = weight; real_t t2 = t * t; real_t t3 = t2 * t; @@ -512,9 +513,9 @@ namespace Godot { return new Vector3 ( - Mathf.Stepify(x, step.x), - Mathf.Stepify(y, step.y), - Mathf.Stepify(z, step.z) + Mathf.Snapped(x, step.x), + Mathf.Snapped(y, step.y), + Mathf.Snapped(z, step.z) ); } diff --git a/modules/mono/glue/arguments_vector.h b/modules/mono/glue/arguments_vector.h index ab48904571..9ba6a05ac6 100644 --- a/modules/mono/glue/arguments_vector.h +++ b/modules/mono/glue/arguments_vector.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp index 544f414cba..34a96eba17 100644 --- a/modules/mono/glue/base_object_glue.cpp +++ b/modules/mono/glue/base_object_glue.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -163,9 +163,9 @@ MonoObject *godot_icall_Object_weakref(Object *p_ptr) { return GDMonoUtils::unmanaged_get_managed(wref.ptr()); } -Error godot_icall_SignalAwaiter_connect(Object *p_source, StringName *p_signal, Object *p_target, MonoObject *p_awaiter) { +int32_t godot_icall_SignalAwaiter_connect(Object *p_source, StringName *p_signal, Object *p_target, MonoObject *p_awaiter) { StringName signal = p_signal ? *p_signal : StringName(); - return gd_mono_connect_signal_awaiter(p_source, signal, p_target, p_awaiter); + return (int32_t)gd_mono_connect_signal_awaiter(p_source, signal, p_target, p_awaiter); } MonoArray *godot_icall_DynamicGodotObject_SetMemberList(Object *p_ptr) { @@ -240,18 +240,18 @@ MonoString *godot_icall_Object_ToString(Object *p_ptr) { } void godot_register_object_icalls() { - mono_add_internal_call("Godot.Object::godot_icall_Object_Ctor", (void *)godot_icall_Object_Ctor); - mono_add_internal_call("Godot.Object::godot_icall_Object_Disposed", (void *)godot_icall_Object_Disposed); - mono_add_internal_call("Godot.Object::godot_icall_Reference_Disposed", (void *)godot_icall_Reference_Disposed); - mono_add_internal_call("Godot.Object::godot_icall_Object_ConnectEventSignals", (void *)godot_icall_Object_ConnectEventSignals); - mono_add_internal_call("Godot.Object::godot_icall_Object_ClassDB_get_method", (void *)godot_icall_Object_ClassDB_get_method); - mono_add_internal_call("Godot.Object::godot_icall_Object_ToString", (void *)godot_icall_Object_ToString); - mono_add_internal_call("Godot.Object::godot_icall_Object_weakref", (void *)godot_icall_Object_weakref); - mono_add_internal_call("Godot.SignalAwaiter::godot_icall_SignalAwaiter_connect", (void *)godot_icall_SignalAwaiter_connect); - mono_add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_SetMemberList", (void *)godot_icall_DynamicGodotObject_SetMemberList); - mono_add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_InvokeMember", (void *)godot_icall_DynamicGodotObject_InvokeMember); - mono_add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_GetMember", (void *)godot_icall_DynamicGodotObject_GetMember); - mono_add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_SetMember", (void *)godot_icall_DynamicGodotObject_SetMember); + GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_Ctor", godot_icall_Object_Ctor); + GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_Disposed", godot_icall_Object_Disposed); + GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Reference_Disposed", godot_icall_Reference_Disposed); + GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_ConnectEventSignals", godot_icall_Object_ConnectEventSignals); + GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_ClassDB_get_method", godot_icall_Object_ClassDB_get_method); + GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_ToString", godot_icall_Object_ToString); + GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_weakref", godot_icall_Object_weakref); + GDMonoUtils::add_internal_call("Godot.SignalAwaiter::godot_icall_SignalAwaiter_connect", godot_icall_SignalAwaiter_connect); + GDMonoUtils::add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_SetMemberList", godot_icall_DynamicGodotObject_SetMemberList); + GDMonoUtils::add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_InvokeMember", godot_icall_DynamicGodotObject_InvokeMember); + GDMonoUtils::add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_GetMember", godot_icall_DynamicGodotObject_GetMember); + GDMonoUtils::add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_SetMember", godot_icall_DynamicGodotObject_SetMember); } #endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp index bb3ea0f730..191f863350 100644 --- a/modules/mono/glue/collections_glue.cpp +++ b/modules/mono/glue/collections_glue.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -47,7 +47,7 @@ void godot_icall_Array_Dtor(Array *ptr) { memdelete(ptr); } -MonoObject *godot_icall_Array_At(Array *ptr, int index) { +MonoObject *godot_icall_Array_At(Array *ptr, int32_t index) { if (index < 0 || index >= ptr->size()) { GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); return nullptr; @@ -55,7 +55,7 @@ MonoObject *godot_icall_Array_At(Array *ptr, int index) { return GDMonoMarshal::variant_to_mono_object(ptr->operator[](index)); } -MonoObject *godot_icall_Array_At_Generic(Array *ptr, int index, uint32_t type_encoding, GDMonoClass *type_class) { +MonoObject *godot_icall_Array_At_Generic(Array *ptr, int32_t index, uint32_t type_encoding, GDMonoClass *type_class) { if (index < 0 || index >= ptr->size()) { GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); return nullptr; @@ -63,7 +63,7 @@ MonoObject *godot_icall_Array_At_Generic(Array *ptr, int index, uint32_t type_en return GDMonoMarshal::variant_to_mono_object(ptr->operator[](index), ManagedType(type_encoding, type_class)); } -void godot_icall_Array_SetAt(Array *ptr, int index, MonoObject *value) { +void godot_icall_Array_SetAt(Array *ptr, int32_t index, MonoObject *value) { if (index < 0 || index >= ptr->size()) { GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); return; @@ -71,11 +71,11 @@ void godot_icall_Array_SetAt(Array *ptr, int index, MonoObject *value) { ptr->operator[](index) = GDMonoMarshal::mono_object_to_variant(value); } -int godot_icall_Array_Count(Array *ptr) { +int32_t godot_icall_Array_Count(Array *ptr) { return ptr->size(); } -int godot_icall_Array_Add(Array *ptr, MonoObject *item) { +int32_t godot_icall_Array_Add(Array *ptr, MonoObject *item) { ptr->append(GDMonoMarshal::mono_object_to_variant(item)); return ptr->size(); } @@ -88,7 +88,7 @@ MonoBoolean godot_icall_Array_Contains(Array *ptr, MonoObject *item) { return ptr->find(GDMonoMarshal::mono_object_to_variant(item)) != -1; } -void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index) { +void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int32_t array_index) { unsigned int count = ptr->size(); if (mono_array_length(array) < (array_index + count)) { @@ -129,11 +129,11 @@ Array *godot_icall_Array_Concatenate(Array *left, Array *right) { return new_array; } -int godot_icall_Array_IndexOf(Array *ptr, MonoObject *item) { +int32_t godot_icall_Array_IndexOf(Array *ptr, MonoObject *item) { return ptr->find(GDMonoMarshal::mono_object_to_variant(item)); } -void godot_icall_Array_Insert(Array *ptr, int index, MonoObject *item) { +void godot_icall_Array_Insert(Array *ptr, int32_t index, MonoObject *item) { if (index < 0 || index > ptr->size()) { GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); return; @@ -150,7 +150,7 @@ MonoBoolean godot_icall_Array_Remove(Array *ptr, MonoObject *item) { return false; } -void godot_icall_Array_RemoveAt(Array *ptr, int index) { +void godot_icall_Array_RemoveAt(Array *ptr, int32_t index) { if (index < 0 || index >= ptr->size()) { GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); return; @@ -158,8 +158,8 @@ void godot_icall_Array_RemoveAt(Array *ptr, int index) { ptr->remove(index); } -Error godot_icall_Array_Resize(Array *ptr, int new_size) { - return ptr->resize(new_size); +int32_t godot_icall_Array_Resize(Array *ptr, int32_t new_size) { + return (int32_t)ptr->resize(new_size); } void godot_icall_Array_Shuffle(Array *ptr) { @@ -226,7 +226,7 @@ Array *godot_icall_Dictionary_Values(Dictionary *ptr) { return memnew(Array(ptr->values())); } -int godot_icall_Dictionary_Count(Dictionary *ptr) { +int32_t godot_icall_Dictionary_Count(Dictionary *ptr) { return ptr->size(); } @@ -308,47 +308,47 @@ MonoString *godot_icall_Dictionary_ToString(Dictionary *ptr) { } void godot_register_collections_icalls() { - mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor", (void *)godot_icall_Array_Ctor); - mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor_MonoArray", (void *)godot_icall_Array_Ctor_MonoArray); - mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Dtor", (void *)godot_icall_Array_Dtor); - mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_At", (void *)godot_icall_Array_At); - mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_At_Generic", (void *)godot_icall_Array_At_Generic); - mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_SetAt", (void *)godot_icall_Array_SetAt); - mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Count", (void *)godot_icall_Array_Count); - mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Add", (void *)godot_icall_Array_Add); - mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Clear", (void *)godot_icall_Array_Clear); - mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Concatenate", (void *)godot_icall_Array_Concatenate); - mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Contains", (void *)godot_icall_Array_Contains); - mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_CopyTo", (void *)godot_icall_Array_CopyTo); - mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Duplicate", (void *)godot_icall_Array_Duplicate); - mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_IndexOf", (void *)godot_icall_Array_IndexOf); - mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Insert", (void *)godot_icall_Array_Insert); - mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Remove", (void *)godot_icall_Array_Remove); - mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_RemoveAt", (void *)godot_icall_Array_RemoveAt); - mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Resize", (void *)godot_icall_Array_Resize); - mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Shuffle", (void *)godot_icall_Array_Shuffle); - mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Generic_GetElementTypeInfo", (void *)godot_icall_Array_Generic_GetElementTypeInfo); - mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_ToString", (void *)godot_icall_Array_ToString); - - mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Ctor", (void *)godot_icall_Dictionary_Ctor); - mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Dtor", (void *)godot_icall_Dictionary_Dtor); - mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_GetValue", (void *)godot_icall_Dictionary_GetValue); - mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_GetValue_Generic", (void *)godot_icall_Dictionary_GetValue_Generic); - mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_SetValue", (void *)godot_icall_Dictionary_SetValue); - mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Keys", (void *)godot_icall_Dictionary_Keys); - mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Values", (void *)godot_icall_Dictionary_Values); - mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Count", (void *)godot_icall_Dictionary_Count); - mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Add", (void *)godot_icall_Dictionary_Add); - mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Clear", (void *)godot_icall_Dictionary_Clear); - mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Contains", (void *)godot_icall_Dictionary_Contains); - mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_ContainsKey", (void *)godot_icall_Dictionary_ContainsKey); - mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Duplicate", (void *)godot_icall_Dictionary_Duplicate); - mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_RemoveKey", (void *)godot_icall_Dictionary_RemoveKey); - mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Remove", (void *)godot_icall_Dictionary_Remove); - mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue", (void *)godot_icall_Dictionary_TryGetValue); - mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue_Generic", (void *)godot_icall_Dictionary_TryGetValue_Generic); - mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Generic_GetValueTypeInfo", (void *)godot_icall_Dictionary_Generic_GetValueTypeInfo); - mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_ToString", (void *)godot_icall_Dictionary_ToString); + GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor", godot_icall_Array_Ctor); + GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor_MonoArray", godot_icall_Array_Ctor_MonoArray); + GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Dtor", godot_icall_Array_Dtor); + GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_At", godot_icall_Array_At); + GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_At_Generic", godot_icall_Array_At_Generic); + GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_SetAt", godot_icall_Array_SetAt); + GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Count", godot_icall_Array_Count); + GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Add", godot_icall_Array_Add); + GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Clear", godot_icall_Array_Clear); + GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Concatenate", godot_icall_Array_Concatenate); + GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Contains", godot_icall_Array_Contains); + GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_CopyTo", godot_icall_Array_CopyTo); + GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Duplicate", godot_icall_Array_Duplicate); + GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_IndexOf", godot_icall_Array_IndexOf); + GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Insert", godot_icall_Array_Insert); + GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Remove", godot_icall_Array_Remove); + GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_RemoveAt", godot_icall_Array_RemoveAt); + GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Resize", godot_icall_Array_Resize); + GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Shuffle", godot_icall_Array_Shuffle); + GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Generic_GetElementTypeInfo", godot_icall_Array_Generic_GetElementTypeInfo); + GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_ToString", godot_icall_Array_ToString); + + GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Ctor", godot_icall_Dictionary_Ctor); + GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Dtor", godot_icall_Dictionary_Dtor); + GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_GetValue", godot_icall_Dictionary_GetValue); + GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_GetValue_Generic", godot_icall_Dictionary_GetValue_Generic); + GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_SetValue", godot_icall_Dictionary_SetValue); + GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Keys", godot_icall_Dictionary_Keys); + GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Values", godot_icall_Dictionary_Values); + GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Count", godot_icall_Dictionary_Count); + GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Add", godot_icall_Dictionary_Add); + GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Clear", godot_icall_Dictionary_Clear); + GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Contains", godot_icall_Dictionary_Contains); + GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_ContainsKey", godot_icall_Dictionary_ContainsKey); + GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Duplicate", godot_icall_Dictionary_Duplicate); + GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_RemoveKey", godot_icall_Dictionary_RemoveKey); + GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Remove", godot_icall_Dictionary_Remove); + GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue", godot_icall_Dictionary_TryGetValue); + GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue_Generic", godot_icall_Dictionary_TryGetValue_Generic); + GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Generic_GetValueTypeInfo", godot_icall_Dictionary_Generic_GetValueTypeInfo); + GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_ToString", godot_icall_Dictionary_ToString); } #endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/gd_glue.cpp b/modules/mono/glue/gd_glue.cpp index 58d8dceb25..a2ff868f65 100644 --- a/modules/mono/glue/gd_glue.cpp +++ b/modules/mono/glue/gd_glue.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -289,33 +289,33 @@ MonoObject *godot_icall_DefaultGodotTaskScheduler() { } void godot_register_gd_icalls() { - mono_add_internal_call("Godot.GD::godot_icall_GD_bytes2var", (void *)godot_icall_GD_bytes2var); - mono_add_internal_call("Godot.GD::godot_icall_GD_convert", (void *)godot_icall_GD_convert); - mono_add_internal_call("Godot.GD::godot_icall_GD_hash", (void *)godot_icall_GD_hash); - mono_add_internal_call("Godot.GD::godot_icall_GD_instance_from_id", (void *)godot_icall_GD_instance_from_id); - mono_add_internal_call("Godot.GD::godot_icall_GD_pusherror", (void *)godot_icall_GD_pusherror); - mono_add_internal_call("Godot.GD::godot_icall_GD_pushwarning", (void *)godot_icall_GD_pushwarning); - mono_add_internal_call("Godot.GD::godot_icall_GD_print", (void *)godot_icall_GD_print); - mono_add_internal_call("Godot.GD::godot_icall_GD_printerr", (void *)godot_icall_GD_printerr); - mono_add_internal_call("Godot.GD::godot_icall_GD_printraw", (void *)godot_icall_GD_printraw); - mono_add_internal_call("Godot.GD::godot_icall_GD_prints", (void *)godot_icall_GD_prints); - mono_add_internal_call("Godot.GD::godot_icall_GD_printt", (void *)godot_icall_GD_printt); - mono_add_internal_call("Godot.GD::godot_icall_GD_randf", (void *)godot_icall_GD_randf); - mono_add_internal_call("Godot.GD::godot_icall_GD_randi", (void *)godot_icall_GD_randi); - mono_add_internal_call("Godot.GD::godot_icall_GD_randomize", (void *)godot_icall_GD_randomize); - mono_add_internal_call("Godot.GD::godot_icall_GD_randf_range", (void *)godot_icall_GD_randf_range); - mono_add_internal_call("Godot.GD::godot_icall_GD_randi_range", (void *)godot_icall_GD_randi_range); - mono_add_internal_call("Godot.GD::godot_icall_GD_rand_seed", (void *)godot_icall_GD_rand_seed); - mono_add_internal_call("Godot.GD::godot_icall_GD_seed", (void *)godot_icall_GD_seed); - mono_add_internal_call("Godot.GD::godot_icall_GD_str", (void *)godot_icall_GD_str); - mono_add_internal_call("Godot.GD::godot_icall_GD_str2var", (void *)godot_icall_GD_str2var); - mono_add_internal_call("Godot.GD::godot_icall_GD_type_exists", (void *)godot_icall_GD_type_exists); - mono_add_internal_call("Godot.GD::godot_icall_GD_var2bytes", (void *)godot_icall_GD_var2bytes); - mono_add_internal_call("Godot.GD::godot_icall_GD_var2str", (void *)godot_icall_GD_var2str); - mono_add_internal_call("Godot.GD::godot_icall_TypeToVariantType", (void *)godot_icall_TypeToVariantType); + GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_bytes2var", godot_icall_GD_bytes2var); + GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_convert", godot_icall_GD_convert); + GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_hash", godot_icall_GD_hash); + GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_instance_from_id", godot_icall_GD_instance_from_id); + GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_pusherror", godot_icall_GD_pusherror); + GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_pushwarning", godot_icall_GD_pushwarning); + GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_print", godot_icall_GD_print); + GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_printerr", godot_icall_GD_printerr); + GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_printraw", godot_icall_GD_printraw); + GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_prints", godot_icall_GD_prints); + GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_printt", godot_icall_GD_printt); + GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randf", godot_icall_GD_randf); + GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randi", godot_icall_GD_randi); + GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randomize", godot_icall_GD_randomize); + GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randf_range", godot_icall_GD_randf_range); + GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randi_range", godot_icall_GD_randi_range); + GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_rand_seed", godot_icall_GD_rand_seed); + GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_seed", godot_icall_GD_seed); + GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_str", godot_icall_GD_str); + GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_str2var", godot_icall_GD_str2var); + GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_type_exists", godot_icall_GD_type_exists); + GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_var2bytes", godot_icall_GD_var2bytes); + GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_var2str", godot_icall_GD_var2str); + GDMonoUtils::add_internal_call("Godot.GD::godot_icall_TypeToVariantType", godot_icall_TypeToVariantType); // Dispatcher - mono_add_internal_call("Godot.Dispatcher::godot_icall_DefaultGodotTaskScheduler", (void *)godot_icall_DefaultGodotTaskScheduler); + GDMonoUtils::add_internal_call("Godot.Dispatcher::godot_icall_DefaultGodotTaskScheduler", godot_icall_DefaultGodotTaskScheduler); } #endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/glue_header.h b/modules/mono/glue/glue_header.h index f4263e286e..3db52d7c30 100644 --- a/modules/mono/glue/glue_header.h +++ b/modules/mono/glue/glue_header.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/glue/nodepath_glue.cpp b/modules/mono/glue/nodepath_glue.cpp index 09c6d8f482..4ddb94e1a8 100644 --- a/modules/mono/glue/nodepath_glue.cpp +++ b/modules/mono/glue/nodepath_glue.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -81,17 +81,17 @@ MonoBoolean godot_icall_NodePath_is_empty(NodePath *p_ptr) { } void godot_register_nodepath_icalls() { - mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_Ctor", (void *)godot_icall_NodePath_Ctor); - mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_Dtor", (void *)godot_icall_NodePath_Dtor); - mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_operator_String", (void *)godot_icall_NodePath_operator_String); - mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_get_as_property_path", (void *)godot_icall_NodePath_get_as_property_path); - mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_get_concatenated_subnames", (void *)godot_icall_NodePath_get_concatenated_subnames); - mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_get_name", (void *)godot_icall_NodePath_get_name); - mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_get_name_count", (void *)godot_icall_NodePath_get_name_count); - mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_get_subname", (void *)godot_icall_NodePath_get_subname); - mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_get_subname_count", (void *)godot_icall_NodePath_get_subname_count); - mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_is_absolute", (void *)godot_icall_NodePath_is_absolute); - mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_is_empty", (void *)godot_icall_NodePath_is_empty); + GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_Ctor", godot_icall_NodePath_Ctor); + GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_Dtor", godot_icall_NodePath_Dtor); + GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_operator_String", godot_icall_NodePath_operator_String); + GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_as_property_path", godot_icall_NodePath_get_as_property_path); + GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_concatenated_subnames", godot_icall_NodePath_get_concatenated_subnames); + GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_name", godot_icall_NodePath_get_name); + GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_name_count", godot_icall_NodePath_get_name_count); + GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_subname", godot_icall_NodePath_get_subname); + GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_subname_count", godot_icall_NodePath_get_subname_count); + GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_is_absolute", godot_icall_NodePath_is_absolute); + GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_is_empty", godot_icall_NodePath_is_empty); } #endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/rid_glue.cpp b/modules/mono/glue/rid_glue.cpp index cb4f26511f..f464e63a81 100644 --- a/modules/mono/glue/rid_glue.cpp +++ b/modules/mono/glue/rid_glue.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -56,9 +56,9 @@ uint32_t godot_icall_RID_get_id(RID *p_ptr) { } void godot_register_rid_icalls() { - mono_add_internal_call("Godot.RID::godot_icall_RID_Ctor", (void *)godot_icall_RID_Ctor); - mono_add_internal_call("Godot.RID::godot_icall_RID_Dtor", (void *)godot_icall_RID_Dtor); - mono_add_internal_call("Godot.RID::godot_icall_RID_get_id", (void *)godot_icall_RID_get_id); + GDMonoUtils::add_internal_call("Godot.RID::godot_icall_RID_Ctor", godot_icall_RID_Ctor); + GDMonoUtils::add_internal_call("Godot.RID::godot_icall_RID_Dtor", godot_icall_RID_Dtor); + GDMonoUtils::add_internal_call("Godot.RID::godot_icall_RID_get_id", godot_icall_RID_get_id); } #endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/scene_tree_glue.cpp b/modules/mono/glue/scene_tree_glue.cpp index 53d6c1436d..5a6fd69db8 100644 --- a/modules/mono/glue/scene_tree_glue.cpp +++ b/modules/mono/glue/scene_tree_glue.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -48,7 +48,7 @@ Array *godot_icall_SceneTree_get_nodes_in_group_Generic(SceneTree *ptr, StringNa ptr->get_nodes_in_group(*group, &nodes); // No need to bother if the group is empty - if (!nodes.empty()) { + if (!nodes.is_empty()) { MonoType *elem_type = mono_reflection_type_get_type(refltype); MonoClass *mono_class = mono_class_from_mono_type(elem_type); GDMonoClass *klass = GDMono::get_singleton()->get_class(mono_class); @@ -80,7 +80,7 @@ Array *godot_icall_SceneTree_get_nodes_in_group_Generic(SceneTree *ptr, StringNa } void godot_register_scene_tree_icalls() { - mono_add_internal_call("Godot.SceneTree::godot_icall_SceneTree_get_nodes_in_group_Generic", (void *)godot_icall_SceneTree_get_nodes_in_group_Generic); + GDMonoUtils::add_internal_call("Godot.SceneTree::godot_icall_SceneTree_get_nodes_in_group_Generic", godot_icall_SceneTree_get_nodes_in_group_Generic); } #endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/string_glue.cpp b/modules/mono/glue/string_glue.cpp index 9271731573..18a9221f89 100644 --- a/modules/mono/glue/string_glue.cpp +++ b/modules/mono/glue/string_glue.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -68,12 +68,12 @@ MonoString *godot_icall_String_sha256_text(MonoString *p_str) { } void godot_register_string_icalls() { - mono_add_internal_call("Godot.StringExtensions::godot_icall_String_md5_buffer", (void *)godot_icall_String_md5_buffer); - mono_add_internal_call("Godot.StringExtensions::godot_icall_String_md5_text", (void *)godot_icall_String_md5_text); - mono_add_internal_call("Godot.StringExtensions::godot_icall_String_rfind", (void *)godot_icall_String_rfind); - mono_add_internal_call("Godot.StringExtensions::godot_icall_String_rfindn", (void *)godot_icall_String_rfindn); - mono_add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_buffer", (void *)godot_icall_String_sha256_buffer); - mono_add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_text", (void *)godot_icall_String_sha256_text); + GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_md5_buffer", godot_icall_String_md5_buffer); + GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_md5_text", godot_icall_String_md5_text); + GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_rfind", godot_icall_String_rfind); + GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_rfindn", godot_icall_String_rfindn); + GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_buffer", godot_icall_String_sha256_buffer); + GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_text", godot_icall_String_sha256_text); } #endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/string_name_glue.cpp b/modules/mono/glue/string_name_glue.cpp index 9323e3bbb3..f537896559 100644 --- a/modules/mono/glue/string_name_glue.cpp +++ b/modules/mono/glue/string_name_glue.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -53,10 +53,10 @@ MonoBoolean godot_icall_StringName_is_empty(StringName *p_ptr) { } void godot_register_string_name_icalls() { - mono_add_internal_call("Godot.StringName::godot_icall_StringName_Ctor", (void *)godot_icall_StringName_Ctor); - mono_add_internal_call("Godot.StringName::godot_icall_StringName_Dtor", (void *)godot_icall_StringName_Dtor); - mono_add_internal_call("Godot.StringName::godot_icall_StringName_operator_String", (void *)godot_icall_StringName_operator_String); - mono_add_internal_call("Godot.StringName::godot_icall_StringName_is_empty", (void *)godot_icall_StringName_is_empty); + GDMonoUtils::add_internal_call("Godot.StringName::godot_icall_StringName_Ctor", godot_icall_StringName_Ctor); + GDMonoUtils::add_internal_call("Godot.StringName::godot_icall_StringName_Dtor", godot_icall_StringName_Dtor); + GDMonoUtils::add_internal_call("Godot.StringName::godot_icall_StringName_operator_String", godot_icall_StringName_operator_String); + GDMonoUtils::add_internal_call("Godot.StringName::godot_icall_StringName_is_empty", godot_icall_StringName_is_empty); } #endif // MONO_GLUE_ENABLED diff --git a/modules/mono/godotsharp_defs.h b/modules/mono/godotsharp_defs.h index 7d57d0fac3..273dba52f9 100644 --- a/modules/mono/godotsharp_defs.h +++ b/modules/mono/godotsharp_defs.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp index 093a935288..a39a6fe381 100644 --- a/modules/mono/godotsharp_dirs.cpp +++ b/modules/mono/godotsharp_dirs.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -146,7 +146,7 @@ private: String appname = ProjectSettings::get_singleton()->get("application/config/name"); String appname_safe = OS::get_singleton()->get_safe_dir_name(appname); - if (appname_safe.empty()) { + if (appname_safe.is_empty()) { appname_safe = "UnnamedProject"; } diff --git a/modules/mono/godotsharp_dirs.h b/modules/mono/godotsharp_dirs.h index 85be506c28..3a3c6f980e 100644 --- a/modules/mono/godotsharp_dirs.h +++ b/modules/mono/godotsharp_dirs.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/managed_callable.cpp b/modules/mono/managed_callable.cpp index dbe9c7fc5d..6d868b527c 100644 --- a/modules/mono/managed_callable.cpp +++ b/modules/mono/managed_callable.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/managed_callable.h b/modules/mono/managed_callable.h index bde1b41781..c620eee60d 100644 --- a/modules/mono/managed_callable.h +++ b/modules/mono/managed_callable.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/mono_gc_handle.cpp b/modules/mono/mono_gc_handle.cpp index 16a6875406..8583065016 100644 --- a/modules/mono/mono_gc_handle.cpp +++ b/modules/mono/mono_gc_handle.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/mono_gc_handle.h b/modules/mono/mono_gc_handle.h index b85dc70af3..f435aab3dd 100644 --- a/modules/mono/mono_gc_handle.h +++ b/modules/mono/mono_gc_handle.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/mono_gd/android_mono_config.h b/modules/mono/mono_gd/android_mono_config.h index 9e304939b2..9d7cfe1b7c 100644 --- a/modules/mono/mono_gd/android_mono_config.h +++ b/modules/mono/mono_gd/android_mono_config.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 772961291c..875d20ebe4 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -142,7 +142,7 @@ void gd_mono_debug_init() { int da_timeout = GLOBAL_DEF("mono/debugger_agent/wait_timeout", 3000); if (Engine::get_singleton()->is_editor_hint() || - ProjectSettings::get_singleton()->get_resource_path().empty() || + ProjectSettings::get_singleton()->get_resource_path().is_empty() || Main::is_project_manager()) { if (da_args.size() == 0) { return; @@ -297,7 +297,7 @@ void GDMono::determine_mono_dirs(String &r_assembly_rootdir, String &r_config_di } #ifdef WINDOWS_ENABLED - if (r_assembly_rootdir.empty() || r_config_dir.empty()) { + if (r_assembly_rootdir.is_empty() || r_config_dir.is_empty()) { ERR_PRINT("Cannot find Mono in the registry."); // Assertion: if they are not set, then they weren't found in the registry CRASH_COND(mono_reg_info.assembly_dir.length() > 0 || mono_reg_info.config_dir.length() > 0); @@ -360,7 +360,7 @@ void GDMono::initialize() { #ifndef TOOLS_ENABLED // Exported games that don't use C# must still work. They likely don't ship with mscorlib. // We only initialize the Mono runtime if we can find mscorlib. Otherwise it would crash. - if (GDMonoAssembly::find_assembly("mscorlib.dll").empty()) { + if (GDMonoAssembly::find_assembly("mscorlib.dll").is_empty()) { print_verbose("Mono: Skipping runtime initialization because 'mscorlib.dll' could not be found"); return; } @@ -944,7 +944,7 @@ void GDMono::_load_api_assemblies() { // 2. Update the API assemblies String update_error = update_api_assemblies_from_prebuilt("Debug", &core_api_assembly.out_of_sync, &editor_api_assembly.out_of_sync); - CRASH_COND_MSG(!update_error.empty(), update_error); + CRASH_COND_MSG(!update_error.is_empty(), update_error); // 3. Load the scripts domain again Error domain_load_err = _load_scripts_domain(); @@ -998,7 +998,7 @@ bool GDMono::_load_project_assembly() { String appname = ProjectSettings::get_singleton()->get("application/config/name"); String appname_safe = OS::get_singleton()->get_safe_dir_name(appname); - if (appname_safe.empty()) { + if (appname_safe.is_empty()) { appname_safe = "UnnamedProject"; } diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h index 969296c44d..5accc21f8e 100644 --- a/modules/mono/mono_gd/gd_mono.h +++ b/modules/mono/mono_gd/gd_mono.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp index 33628b3ce3..c6920814b9 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.cpp +++ b/modules/mono/mono_gd/gd_mono_assembly.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -48,20 +48,20 @@ Vector<String> GDMonoAssembly::search_dirs; void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config, const String &p_custom_bcl_dir) { String framework_dir; - if (!p_custom_bcl_dir.empty()) { + if (!p_custom_bcl_dir.is_empty()) { framework_dir = p_custom_bcl_dir; } else if (mono_assembly_getrootdir()) { framework_dir = String::utf8(mono_assembly_getrootdir()).plus_file("mono").plus_file("4.5"); } - if (!framework_dir.empty()) { + if (!framework_dir.is_empty()) { r_search_dirs.push_back(framework_dir); r_search_dirs.push_back(framework_dir.plus_file("Facades")); } #if !defined(TOOLS_ENABLED) String data_game_assemblies_dir = GodotSharpDirs::get_data_game_assemblies_dir(); - if (!data_game_assemblies_dir.empty()) { + if (!data_game_assemblies_dir.is_empty()) { r_search_dirs.push_back(data_game_assemblies_dir); } #endif @@ -72,7 +72,7 @@ void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const Strin r_search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_dir()); } - if (p_custom_config.empty()) { + if (p_custom_config.is_empty()) { r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_dir()); } else { String api_config = p_custom_config == "ExportRelease" ? "Release" : "Debug"; @@ -230,7 +230,7 @@ void GDMonoAssembly::initialize() { MonoAssembly *GDMonoAssembly::_real_load_assembly_from(const String &p_path, bool p_refonly, MonoAssemblyName *p_aname) { Vector<uint8_t> data = FileAccess::get_file_as_array(p_path); - ERR_FAIL_COND_V_MSG(data.empty(), nullptr, "Could read the assembly in the specified location"); + ERR_FAIL_COND_V_MSG(data.is_empty(), nullptr, "Could read the assembly in the specified location"); String image_filename; @@ -423,7 +423,7 @@ GDMonoClass *GDMonoAssembly::get_object_derived_class(const StringName &p_class) match = current; } - while (!nested_classes.empty()) { + while (!nested_classes.is_empty()) { GDMonoClass *current_nested = nested_classes.front()->get(); nested_classes.pop_front(); diff --git a/modules/mono/mono_gd/gd_mono_assembly.h b/modules/mono/mono_gd/gd_mono_assembly.h index fc10480e07..350fcf3210 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.h +++ b/modules/mono/mono_gd/gd_mono_assembly.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp index 3f51c6523b..aea467660f 100644 --- a/modules/mono/mono_gd/gd_mono_cache.cpp +++ b/modules/mono/mono_gd/gd_mono_cache.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h index 9dfa5769be..fb75cb4b1c 100644 --- a/modules/mono/mono_gd/gd_mono_cache.h +++ b/modules/mono/mono_gd/gd_mono_cache.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp index 6575cbc1c8..0ed7fcf375 100644 --- a/modules/mono/mono_gd/gd_mono_class.cpp +++ b/modules/mono/mono_gd/gd_mono_class.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -290,7 +290,7 @@ bool GDMonoClass::has_public_parameterless_ctor() { return ctor && ctor->get_visibility() == IMonoClassMember::PUBLIC; } -GDMonoMethod *GDMonoClass::get_method(const StringName &p_name, int p_params_count) { +GDMonoMethod *GDMonoClass::get_method(const StringName &p_name, uint16_t p_params_count) { MethodKey key = MethodKey(p_name, p_params_count); GDMonoMethod **match = methods.getptr(key); @@ -330,7 +330,7 @@ GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method, const StringName return get_method(p_raw_method, p_name, params_count); } -GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method, const StringName &p_name, int p_params_count) { +GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method, const StringName &p_name, uint16_t p_params_count) { ERR_FAIL_NULL_V(p_raw_method, nullptr); MethodKey key = MethodKey(p_name, p_params_count); diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h index 87db2fa033..daea75bae8 100644 --- a/modules/mono/mono_gd/gd_mono_class.h +++ b/modules/mono/mono_gd/gd_mono_class.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -59,13 +59,12 @@ class GDMonoClass { MethodKey() {} - MethodKey(const StringName &p_name, int p_params_count) { - name = p_name; - params_count = p_params_count; + MethodKey(const StringName &p_name, uint16_t p_params_count) : + name(p_name), params_count(p_params_count) { } StringName name; - int params_count; + uint16_t params_count = 0; }; StringName namespace_name; @@ -139,10 +138,10 @@ public: bool implements_interface(GDMonoClass *p_interface); bool has_public_parameterless_ctor(); - GDMonoMethod *get_method(const StringName &p_name, int p_params_count = 0); + GDMonoMethod *get_method(const StringName &p_name, uint16_t p_params_count = 0); GDMonoMethod *get_method(MonoMethod *p_raw_method); GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name); - GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name, int p_params_count); + GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name, uint16_t p_params_count); GDMonoMethod *get_method_with_desc(const String &p_description, bool p_include_namespace); GDMonoField *get_field(const StringName &p_name); diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp index 00a1e1e507..d91bb8210f 100644 --- a/modules/mono/mono_gd/gd_mono_field.cpp +++ b/modules/mono/mono_gd/gd_mono_field.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -46,29 +46,15 @@ void GDMonoField::set_value_raw(MonoObject *p_object, void *p_ptr) { } void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_value) { -#define SET_FROM_STRUCT(m_type) \ - { \ - GDMonoMarshal::M_##m_type from = MARSHALLED_OUT(m_type, p_value.operator ::m_type()); \ - mono_field_set_value(p_object, mono_field, &from); \ - } - -#define SET_FROM_ARRAY(m_type) \ - { \ - MonoArray *managed = GDMonoMarshal::m_type##_to_mono_array(p_value.operator ::m_type()); \ - mono_field_set_value(p_object, mono_field, managed); \ - } - switch (type.type_encoding) { case MONO_TYPE_BOOLEAN: { MonoBoolean val = p_value.operator bool(); mono_field_set_value(p_object, mono_field, &val); } break; - case MONO_TYPE_CHAR: { int16_t val = p_value.operator unsigned short(); mono_field_set_value(p_object, mono_field, &val); } break; - case MONO_TYPE_I1: { int8_t val = p_value.operator signed char(); mono_field_set_value(p_object, mono_field, &val); @@ -85,7 +71,6 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ int64_t val = p_value.operator int64_t(); mono_field_set_value(p_object, mono_field, &val); } break; - case MONO_TYPE_U1: { uint8_t val = p_value.operator unsigned char(); mono_field_set_value(p_object, mono_field, &val); @@ -102,93 +87,92 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ uint64_t val = p_value.operator uint64_t(); mono_field_set_value(p_object, mono_field, &val); } break; - case MONO_TYPE_R4: { float val = p_value.operator float(); mono_field_set_value(p_object, mono_field, &val); } break; - case MONO_TYPE_R8: { double val = p_value.operator double(); mono_field_set_value(p_object, mono_field, &val); } break; - - case MONO_TYPE_STRING: { - if (p_value.get_type() == Variant::NIL) { - // Otherwise, Variant -> String would return the string "Null" - MonoString *mono_string = nullptr; - mono_field_set_value(p_object, mono_field, mono_string); - } else { - MonoString *mono_string = GDMonoMarshal::mono_string_from_godot(p_value); - mono_field_set_value(p_object, mono_field, mono_string); - } - } break; - case MONO_TYPE_VALUETYPE: { GDMonoClass *tclass = type.type_class; if (tclass == CACHED_CLASS(Vector2)) { - SET_FROM_STRUCT(Vector2); + GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_value.operator ::Vector2()); + mono_field_set_value(p_object, mono_field, &from); break; } if (tclass == CACHED_CLASS(Vector2i)) { - SET_FROM_STRUCT(Vector2i); + GDMonoMarshal::M_Vector2i from = MARSHALLED_OUT(Vector2i, p_value.operator ::Vector2i()); + mono_field_set_value(p_object, mono_field, &from); break; } if (tclass == CACHED_CLASS(Rect2)) { - SET_FROM_STRUCT(Rect2); + GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_value.operator ::Rect2()); + mono_field_set_value(p_object, mono_field, &from); break; } if (tclass == CACHED_CLASS(Rect2i)) { - SET_FROM_STRUCT(Rect2i); + GDMonoMarshal::M_Rect2i from = MARSHALLED_OUT(Rect2i, p_value.operator ::Rect2i()); + mono_field_set_value(p_object, mono_field, &from); break; } if (tclass == CACHED_CLASS(Transform2D)) { - SET_FROM_STRUCT(Transform2D); + GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_value.operator ::Transform2D()); + mono_field_set_value(p_object, mono_field, &from); break; } if (tclass == CACHED_CLASS(Vector3)) { - SET_FROM_STRUCT(Vector3); + GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_value.operator ::Vector3()); + mono_field_set_value(p_object, mono_field, &from); break; } if (tclass == CACHED_CLASS(Vector3i)) { - SET_FROM_STRUCT(Vector3i); + GDMonoMarshal::M_Vector3i from = MARSHALLED_OUT(Vector3i, p_value.operator ::Vector3i()); + mono_field_set_value(p_object, mono_field, &from); break; } if (tclass == CACHED_CLASS(Basis)) { - SET_FROM_STRUCT(Basis); + GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_value.operator ::Basis()); + mono_field_set_value(p_object, mono_field, &from); break; } if (tclass == CACHED_CLASS(Quat)) { - SET_FROM_STRUCT(Quat); + GDMonoMarshal::M_Quat from = MARSHALLED_OUT(Quat, p_value.operator ::Quat()); + mono_field_set_value(p_object, mono_field, &from); break; } if (tclass == CACHED_CLASS(Transform)) { - SET_FROM_STRUCT(Transform); + GDMonoMarshal::M_Transform from = MARSHALLED_OUT(Transform, p_value.operator ::Transform()); + mono_field_set_value(p_object, mono_field, &from); break; } if (tclass == CACHED_CLASS(AABB)) { - SET_FROM_STRUCT(AABB); + GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_value.operator ::AABB()); + mono_field_set_value(p_object, mono_field, &from); break; } if (tclass == CACHED_CLASS(Color)) { - SET_FROM_STRUCT(Color); + GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_value.operator ::Color()); + mono_field_set_value(p_object, mono_field, &from); break; } if (tclass == CACHED_CLASS(Plane)) { - SET_FROM_STRUCT(Plane); + GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_value.operator ::Plane()); + mono_field_set_value(p_object, mono_field, &from); break; } @@ -267,118 +251,35 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ ERR_FAIL_MSG("Attempted to set the value of a field of unmarshallable type: '" + tclass->get_name() + "'."); } break; - + case MONO_TYPE_STRING: { + if (p_value.get_type() == Variant::NIL) { + // Otherwise, Variant -> String would return the string "Null" + MonoString *mono_string = nullptr; + mono_field_set_value(p_object, mono_field, mono_string); + } else { + MonoString *mono_string = GDMonoMarshal::mono_string_from_godot(p_value); + mono_field_set_value(p_object, mono_field, mono_string); + } + } break; case MONO_TYPE_ARRAY: case MONO_TYPE_SZARRAY: { - MonoArrayType *array_type = mono_type_get_array_type(type.type_class->get_mono_type()); - - if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) { - SET_FROM_ARRAY(Array); - break; - } - - if (array_type->eklass == CACHED_CLASS_RAW(uint8_t)) { - SET_FROM_ARRAY(PackedByteArray); - break; - } - - if (array_type->eklass == CACHED_CLASS_RAW(int32_t)) { - SET_FROM_ARRAY(PackedInt32Array); - break; - } - - if (array_type->eklass == CACHED_CLASS_RAW(int64_t)) { - SET_FROM_ARRAY(PackedInt64Array); - break; - } - - if (array_type->eklass == CACHED_CLASS_RAW(float)) { - SET_FROM_ARRAY(PackedFloat32Array); - break; - } - - if (array_type->eklass == CACHED_CLASS_RAW(double)) { - SET_FROM_ARRAY(PackedFloat64Array); - break; - } - - if (array_type->eklass == CACHED_CLASS_RAW(String)) { - SET_FROM_ARRAY(PackedStringArray); - break; - } - - if (array_type->eklass == CACHED_CLASS_RAW(Vector2)) { - SET_FROM_ARRAY(PackedVector2Array); - break; - } - - if (array_type->eklass == CACHED_CLASS_RAW(Vector3)) { - SET_FROM_ARRAY(PackedVector3Array); - break; - } - - if (array_type->eklass == CACHED_CLASS_RAW(Color)) { - SET_FROM_ARRAY(PackedColorArray); - break; - } - - GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass); - if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class)) { - MonoArray *managed = GDMonoMarshal::Array_to_mono_array(p_value.operator ::Array(), array_type_class); + MonoArray *managed = GDMonoMarshal::variant_to_mono_array(p_value, type.type_class); + if (likely(managed != nullptr)) { mono_field_set_value(p_object, mono_field, managed); - break; } - - ERR_FAIL_MSG("Attempted to convert Variant to a managed array of unmarshallable element type."); } break; - case MONO_TYPE_CLASS: { - GDMonoClass *type_class = type.type_class; - - // GodotObject - if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) { - MonoObject *managed = GDMonoUtils::unmanaged_get_managed(p_value.operator Object *()); + MonoObject *managed = GDMonoMarshal::variant_to_mono_object_of_class(p_value, type.type_class); + if (likely(managed != nullptr)) { mono_field_set_value(p_object, mono_field, managed); - break; } - - if (CACHED_CLASS(StringName) == type_class) { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator StringName()); - mono_field_set_value(p_object, mono_field, managed); - break; - } - - if (CACHED_CLASS(NodePath) == type_class) { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator NodePath()); - mono_field_set_value(p_object, mono_field, managed); - break; - } - - if (CACHED_CLASS(RID) == type_class) { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator ::RID()); - mono_field_set_value(p_object, mono_field, managed); - break; - } - - // Godot.Collections.Dictionary or IDictionary - if (CACHED_CLASS(Dictionary) == type_class || type_class == CACHED_CLASS(System_Collections_IDictionary)) { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary)); - mono_field_set_value(p_object, mono_field, managed); - break; - } - - // Godot.Collections.Array or ICollection or IEnumerable - if (CACHED_CLASS(Array) == type_class || - type_class == CACHED_CLASS(System_Collections_ICollection) || - type_class == CACHED_CLASS(System_Collections_IEnumerable)) { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array)); + } break; + case MONO_TYPE_GENERICINST: { + MonoObject *managed = GDMonoMarshal::variant_to_mono_object_of_genericinst(p_value, type.type_class); + if (likely(managed != nullptr)) { mono_field_set_value(p_object, mono_field, managed); - break; } - - ERR_FAIL_MSG("Attempted to set the value of a field of unmarshallable type: '" + type_class->get_name() + "'."); } break; - case MONO_TYPE_OBJECT: { // Variant switch (p_value.get_type()) { @@ -404,43 +305,56 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ mono_field_set_value(p_object, mono_field, mono_string); } break; case Variant::VECTOR2: { - SET_FROM_STRUCT(Vector2); + GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_value.operator ::Vector2()); + mono_field_set_value(p_object, mono_field, &from); } break; case Variant::VECTOR2I: { - SET_FROM_STRUCT(Vector2i); + GDMonoMarshal::M_Vector2i from = MARSHALLED_OUT(Vector2i, p_value.operator ::Vector2i()); + mono_field_set_value(p_object, mono_field, &from); } break; case Variant::RECT2: { - SET_FROM_STRUCT(Rect2); + GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_value.operator ::Rect2()); + mono_field_set_value(p_object, mono_field, &from); } break; case Variant::RECT2I: { - SET_FROM_STRUCT(Rect2i); + GDMonoMarshal::M_Rect2i from = MARSHALLED_OUT(Rect2i, p_value.operator ::Rect2i()); + mono_field_set_value(p_object, mono_field, &from); } break; case Variant::VECTOR3: { - SET_FROM_STRUCT(Vector3); + GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_value.operator ::Vector3()); + mono_field_set_value(p_object, mono_field, &from); } break; case Variant::VECTOR3I: { - SET_FROM_STRUCT(Vector3i); + GDMonoMarshal::M_Vector3i from = MARSHALLED_OUT(Vector3i, p_value.operator ::Vector3i()); + mono_field_set_value(p_object, mono_field, &from); } break; case Variant::TRANSFORM2D: { - SET_FROM_STRUCT(Transform2D); + GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_value.operator ::Transform2D()); + mono_field_set_value(p_object, mono_field, &from); } break; case Variant::PLANE: { - SET_FROM_STRUCT(Plane); + GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_value.operator ::Plane()); + mono_field_set_value(p_object, mono_field, &from); } break; case Variant::QUAT: { - SET_FROM_STRUCT(Quat); + GDMonoMarshal::M_Quat from = MARSHALLED_OUT(Quat, p_value.operator ::Quat()); + mono_field_set_value(p_object, mono_field, &from); } break; case Variant::AABB: { - SET_FROM_STRUCT(AABB); + GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_value.operator ::AABB()); + mono_field_set_value(p_object, mono_field, &from); } break; case Variant::BASIS: { - SET_FROM_STRUCT(Basis); + GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_value.operator ::Basis()); + mono_field_set_value(p_object, mono_field, &from); } break; case Variant::TRANSFORM: { - SET_FROM_STRUCT(Transform); + GDMonoMarshal::M_Transform from = MARSHALLED_OUT(Transform, p_value.operator ::Transform()); + mono_field_set_value(p_object, mono_field, &from); } break; case Variant::COLOR: { - SET_FROM_STRUCT(Color); + GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_value.operator ::Color()); + mono_field_set_value(p_object, mono_field, &from); } break; case Variant::STRING_NAME: { MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator StringName()); @@ -475,106 +389,49 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ mono_field_set_value(p_object, mono_field, managed); } break; case Variant::PACKED_BYTE_ARRAY: { - SET_FROM_ARRAY(PackedByteArray); + MonoArray *managed = GDMonoMarshal::PackedByteArray_to_mono_array(p_value.operator ::PackedByteArray()); + mono_field_set_value(p_object, mono_field, managed); } break; case Variant::PACKED_INT32_ARRAY: { - SET_FROM_ARRAY(PackedInt32Array); + MonoArray *managed = GDMonoMarshal::PackedInt32Array_to_mono_array(p_value.operator ::PackedInt32Array()); + mono_field_set_value(p_object, mono_field, managed); } break; case Variant::PACKED_INT64_ARRAY: { - SET_FROM_ARRAY(PackedInt64Array); + MonoArray *managed = GDMonoMarshal::PackedInt64Array_to_mono_array(p_value.operator ::PackedInt64Array()); + mono_field_set_value(p_object, mono_field, managed); } break; case Variant::PACKED_FLOAT32_ARRAY: { - SET_FROM_ARRAY(PackedFloat32Array); + MonoArray *managed = GDMonoMarshal::PackedFloat32Array_to_mono_array(p_value.operator ::PackedFloat32Array()); + mono_field_set_value(p_object, mono_field, managed); } break; case Variant::PACKED_FLOAT64_ARRAY: { - SET_FROM_ARRAY(PackedFloat64Array); + MonoArray *managed = GDMonoMarshal::PackedFloat64Array_to_mono_array(p_value.operator ::PackedFloat64Array()); + mono_field_set_value(p_object, mono_field, managed); } break; case Variant::PACKED_STRING_ARRAY: { - SET_FROM_ARRAY(PackedStringArray); + MonoArray *managed = GDMonoMarshal::PackedStringArray_to_mono_array(p_value.operator ::PackedStringArray()); + mono_field_set_value(p_object, mono_field, managed); } break; case Variant::PACKED_VECTOR2_ARRAY: { - SET_FROM_ARRAY(PackedVector2Array); + MonoArray *managed = GDMonoMarshal::PackedVector2Array_to_mono_array(p_value.operator ::PackedVector2Array()); + mono_field_set_value(p_object, mono_field, managed); } break; case Variant::PACKED_VECTOR3_ARRAY: { - SET_FROM_ARRAY(PackedVector3Array); + MonoArray *managed = GDMonoMarshal::PackedVector3Array_to_mono_array(p_value.operator ::PackedVector3Array()); + mono_field_set_value(p_object, mono_field, managed); } break; case Variant::PACKED_COLOR_ARRAY: { - SET_FROM_ARRAY(PackedColorArray); + MonoArray *managed = GDMonoMarshal::PackedColorArray_to_mono_array(p_value.operator ::PackedColorArray()); + mono_field_set_value(p_object, mono_field, managed); } break; default: break; } } break; - - case MONO_TYPE_GENERICINST: { - MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type.type_class->get_mono_type()); - - // Godot.Collections.Dictionary<TKey, TValue> - if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), type.type_class); - mono_field_set_value(p_object, mono_field, managed); - break; - } - - // Godot.Collections.Array<T> - if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), type.type_class); - mono_field_set_value(p_object, mono_field, managed); - break; - } - - // System.Collections.Generic.Dictionary<TKey, TValue> - if (GDMonoUtils::Marshal::type_is_system_generic_dictionary(reftype)) { - MonoReflectionType *key_reftype = nullptr; - MonoReflectionType *value_reftype = nullptr; - GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype); - MonoObject *managed = GDMonoMarshal::Dictionary_to_system_generic_dict(p_value.operator Dictionary(), - type.type_class, key_reftype, value_reftype); - mono_field_set_value(p_object, mono_field, managed); - break; - } - - // System.Collections.Generic.List<T> - if (GDMonoUtils::Marshal::type_is_system_generic_list(reftype)) { - MonoReflectionType *elem_reftype = nullptr; - GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype); - MonoObject *managed = GDMonoMarshal::Array_to_system_generic_list(p_value.operator Array(), - type.type_class, elem_reftype); - mono_field_set_value(p_object, mono_field, managed); - break; - } - - // IDictionary<TKey, TValue> - if (GDMonoUtils::Marshal::type_is_generic_idictionary(reftype)) { - MonoReflectionType *key_reftype; - MonoReflectionType *value_reftype; - GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype); - GDMonoClass *godot_dict_class = GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype); - - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), godot_dict_class); - mono_field_set_value(p_object, mono_field, managed); - break; - } - - // ICollection<T> or IEnumerable<T> - if (GDMonoUtils::Marshal::type_is_generic_icollection(reftype) || GDMonoUtils::Marshal::type_is_generic_ienumerable(reftype)) { - MonoReflectionType *elem_reftype; - GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype); - GDMonoClass *godot_array_class = GDMonoUtils::Marshal::make_generic_array_type(elem_reftype); - - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), godot_array_class); - mono_field_set_value(p_object, mono_field, managed); - break; - } - } break; - default: { ERR_PRINT("Attempted to set the value of a field of unexpected type encoding: " + itos(type.type_encoding) + "."); } break; } - -#undef SET_FROM_ARRAY_AND_BREAK -#undef SET_FROM_STRUCT_AND_BREAK } MonoObject *GDMonoField::get_value(MonoObject *p_object) { diff --git a/modules/mono/mono_gd/gd_mono_field.h b/modules/mono/mono_gd/gd_mono_field.h index 5b40b439f9..ed5078c673 100644 --- a/modules/mono/mono_gd/gd_mono_field.h +++ b/modules/mono/mono_gd/gd_mono_field.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/mono_gd/gd_mono_header.h b/modules/mono/mono_gd/gd_mono_header.h index ffb56e7cec..483030610f 100644 --- a/modules/mono/mono_gd/gd_mono_header.h +++ b/modules/mono/mono_gd/gd_mono_header.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp index 82f916e8c5..65e2680905 100644 --- a/modules/mono/mono_gd/gd_mono_internals.cpp +++ b/modules/mono/mono_gd/gd_mono_internals.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/mono_gd/gd_mono_internals.h b/modules/mono/mono_gd/gd_mono_internals.h index 0fd6250785..34d2d35b2d 100644 --- a/modules/mono/mono_gd/gd_mono_internals.h +++ b/modules/mono/mono_gd/gd_mono_internals.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/mono_gd/gd_mono_log.cpp b/modules/mono/mono_gd/gd_mono_log.cpp index b8ee0204c4..e1d283242c 100644 --- a/modules/mono/mono_gd/gd_mono_log.cpp +++ b/modules/mono/mono_gd/gd_mono_log.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -160,13 +160,13 @@ void GDMonoLog::initialize() { OS::Date date_now = OS::get_singleton()->get_date(); OS::Time time_now = OS::get_singleton()->get_time(); - String log_file_name = str_format("%d_%02d_%02d %02d.%02d.%02d", + String log_file_name = str_format("%04d-%02d-%02d_%02d.%02d.%02d", date_now.year, date_now.month, date_now.day, time_now.hour, time_now.min, time_now.sec); - log_file_name += str_format(" (%d)", OS::get_singleton()->get_process_id()); + log_file_name += str_format("_%d", OS::get_singleton()->get_process_id()); - log_file_name += ".txt"; + log_file_name += ".log"; log_file_path = logs_dir.plus_file(log_file_name); diff --git a/modules/mono/mono_gd/gd_mono_log.h b/modules/mono/mono_gd/gd_mono_log.h index 3a52316060..9a95e3cb0a 100644 --- a/modules/mono/mono_gd/gd_mono_log.h +++ b/modules/mono/mono_gd/gd_mono_log.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp index eee880ba60..57fbf5b7e1 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -311,152 +311,590 @@ bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_ return false; } -MonoObject *variant_to_mono_object(const Variant *p_var) { - ManagedType type; +MonoString *variant_to_mono_string(const Variant &p_var) { + if (p_var.get_type() == Variant::NIL) { + return nullptr; // Otherwise, Variant -> String would return the string "Null" + } + return mono_string_from_godot(p_var.operator String()); +} + +MonoArray *variant_to_mono_array(const Variant &p_var, GDMonoClass *p_type_class) { + MonoArrayType *array_type = mono_type_get_array_type(p_type_class->get_mono_type()); + + if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) { + return Array_to_mono_array(p_var.operator Array()); + } + + if (array_type->eklass == CACHED_CLASS_RAW(uint8_t)) { + return PackedByteArray_to_mono_array(p_var.operator PackedByteArray()); + } + + if (array_type->eklass == CACHED_CLASS_RAW(int32_t)) { + return PackedInt32Array_to_mono_array(p_var.operator PackedInt32Array()); + } + + if (array_type->eklass == CACHED_CLASS_RAW(int64_t)) { + return PackedInt64Array_to_mono_array(p_var.operator PackedInt64Array()); + } + + if (array_type->eklass == CACHED_CLASS_RAW(float)) { + return PackedFloat32Array_to_mono_array(p_var.operator PackedFloat32Array()); + } + + if (array_type->eklass == CACHED_CLASS_RAW(double)) { + return PackedFloat64Array_to_mono_array(p_var.operator PackedFloat64Array()); + } + + if (array_type->eklass == CACHED_CLASS_RAW(String)) { + return PackedStringArray_to_mono_array(p_var.operator PackedStringArray()); + } + + if (array_type->eklass == CACHED_CLASS_RAW(Vector2)) { + return PackedVector2Array_to_mono_array(p_var.operator PackedVector2Array()); + } + + if (array_type->eklass == CACHED_CLASS_RAW(Vector3)) { + return PackedVector3Array_to_mono_array(p_var.operator PackedVector3Array()); + } + + if (array_type->eklass == CACHED_CLASS_RAW(Color)) { + return PackedColorArray_to_mono_array(p_var.operator PackedColorArray()); + } + + if (mono_class_is_assignable_from(CACHED_CLASS(GodotObject)->get_mono_ptr(), array_type->eklass)) { + return Array_to_mono_array(p_var.operator ::Array(), array_type->eklass); + } + + ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to array of unsupported element type:" + + GDMonoClass::get_full_name(array_type->eklass) + "'."); +} + +MonoObject *variant_to_mono_object_of_class(const Variant &p_var, GDMonoClass *p_type_class) { + // GodotObject + if (CACHED_CLASS(GodotObject)->is_assignable_from(p_type_class)) { + return GDMonoUtils::unmanaged_get_managed(p_var.operator Object *()); + } + + if (CACHED_CLASS(StringName) == p_type_class) { + return GDMonoUtils::create_managed_from(p_var.operator StringName()); + } + + if (CACHED_CLASS(NodePath) == p_type_class) { + return GDMonoUtils::create_managed_from(p_var.operator NodePath()); + } - type.type_encoding = MONO_TYPE_OBJECT; - // type.type_class is not needed when we specify the MONO_TYPE_OBJECT encoding + if (CACHED_CLASS(RID) == p_type_class) { + return GDMonoUtils::create_managed_from(p_var.operator ::RID()); + } + + // Godot.Collections.Dictionary or IDictionary + if (CACHED_CLASS(Dictionary) == p_type_class || CACHED_CLASS(System_Collections_IDictionary) == p_type_class) { + return GDMonoUtils::create_managed_from(p_var.operator Dictionary(), CACHED_CLASS(Dictionary)); + } - return variant_to_mono_object(p_var, type); + // Godot.Collections.Array or ICollection or IEnumerable + if (CACHED_CLASS(Array) == p_type_class || + CACHED_CLASS(System_Collections_ICollection) == p_type_class || + CACHED_CLASS(System_Collections_IEnumerable) == p_type_class) { + return GDMonoUtils::create_managed_from(p_var.operator Array(), CACHED_CLASS(Array)); + } + + ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported type: '" + + p_type_class->get_full_name() + "'."); } -MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_type) { +MonoObject *variant_to_mono_object_of_genericinst(const Variant &p_var, GDMonoClass *p_type_class) { + MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type_class->get_mono_type()); + + // Godot.Collections.Dictionary<TKey, TValue> + if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) { + return GDMonoUtils::create_managed_from(p_var.operator Dictionary(), p_type_class); + } + + // Godot.Collections.Array<T> + if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) { + return GDMonoUtils::create_managed_from(p_var.operator Array(), p_type_class); + } + + // System.Collections.Generic.Dictionary<TKey, TValue> + if (GDMonoUtils::Marshal::type_is_system_generic_dictionary(reftype)) { + MonoReflectionType *key_reftype = nullptr; + MonoReflectionType *value_reftype = nullptr; + GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype); + return Dictionary_to_system_generic_dict(p_var.operator Dictionary(), p_type_class, key_reftype, value_reftype); + } + + // System.Collections.Generic.List<T> + if (GDMonoUtils::Marshal::type_is_system_generic_list(reftype)) { + MonoReflectionType *elem_reftype = nullptr; + GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype); + return Array_to_system_generic_list(p_var.operator Array(), p_type_class, elem_reftype); + } + + // IDictionary<TKey, TValue> + if (GDMonoUtils::Marshal::type_is_generic_idictionary(reftype)) { + MonoReflectionType *key_reftype; + MonoReflectionType *value_reftype; + GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype); + GDMonoClass *godot_dict_class = GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype); + + return GDMonoUtils::create_managed_from(p_var.operator Dictionary(), godot_dict_class); + } + + // ICollection<T> or IEnumerable<T> + if (GDMonoUtils::Marshal::type_is_generic_icollection(reftype) || GDMonoUtils::Marshal::type_is_generic_ienumerable(reftype)) { + MonoReflectionType *elem_reftype; + GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype); + GDMonoClass *godot_array_class = GDMonoUtils::Marshal::make_generic_array_type(elem_reftype); + + return GDMonoUtils::create_managed_from(p_var.operator Array(), godot_array_class); + } + + ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported generic type: '" + + p_type_class->get_full_name() + "'."); +} + +MonoObject *variant_to_mono_object(const Variant &p_var) { + // Variant + switch (p_var.get_type()) { + case Variant::BOOL: { + MonoBoolean val = p_var.operator bool(); + return BOX_BOOLEAN(val); + } + case Variant::INT: { + int64_t val = p_var.operator int64_t(); + return BOX_INT64(val); + } + case Variant::FLOAT: { +#ifdef REAL_T_IS_DOUBLE + double val = p_var.operator double(); + return BOX_DOUBLE(val); +#else + float val = p_var.operator float(); + return BOX_FLOAT(val); +#endif + } + case Variant::STRING: + return (MonoObject *)mono_string_from_godot(p_var.operator String()); + case Variant::VECTOR2: { + GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_var.operator ::Vector2()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2), &from); + } + case Variant::VECTOR2I: { + GDMonoMarshal::M_Vector2i from = MARSHALLED_OUT(Vector2i, p_var.operator ::Vector2i()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2i), &from); + } + case Variant::RECT2: { + GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_var.operator ::Rect2()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2), &from); + } + case Variant::RECT2I: { + GDMonoMarshal::M_Rect2i from = MARSHALLED_OUT(Rect2i, p_var.operator ::Rect2i()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2i), &from); + } + case Variant::VECTOR3: { + GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_var.operator ::Vector3()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3), &from); + } + case Variant::VECTOR3I: { + GDMonoMarshal::M_Vector3i from = MARSHALLED_OUT(Vector3i, p_var.operator ::Vector3i()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3i), &from); + } + case Variant::TRANSFORM2D: { + GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_var.operator ::Transform2D()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform2D), &from); + } + case Variant::PLANE: { + GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_var.operator ::Plane()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Plane), &from); + } + case Variant::QUAT: { + GDMonoMarshal::M_Quat from = MARSHALLED_OUT(Quat, p_var.operator ::Quat()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Quat), &from); + } + case Variant::AABB: { + GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_var.operator ::AABB()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(AABB), &from); + } + case Variant::BASIS: { + GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_var.operator ::Basis()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Basis), &from); + } + case Variant::TRANSFORM: { + GDMonoMarshal::M_Transform from = MARSHALLED_OUT(Transform, p_var.operator ::Transform()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform), &from); + } + case Variant::COLOR: { + GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_var.operator ::Color()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Color), &from); + } + case Variant::STRING_NAME: + return GDMonoUtils::create_managed_from(p_var.operator StringName()); + case Variant::NODE_PATH: + return GDMonoUtils::create_managed_from(p_var.operator NodePath()); + case Variant::RID: + return GDMonoUtils::create_managed_from(p_var.operator ::RID()); + case Variant::OBJECT: + return GDMonoUtils::unmanaged_get_managed(p_var.operator Object *()); + case Variant::CALLABLE: { + GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var.operator Callable()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Callable), &from); + } + case Variant::SIGNAL: { + GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var.operator Signal()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(SignalInfo), &from); + } + case Variant::DICTIONARY: + return GDMonoUtils::create_managed_from(p_var.operator Dictionary(), CACHED_CLASS(Dictionary)); + case Variant::ARRAY: + return GDMonoUtils::create_managed_from(p_var.operator Array(), CACHED_CLASS(Array)); + case Variant::PACKED_BYTE_ARRAY: + return (MonoObject *)PackedByteArray_to_mono_array(p_var.operator PackedByteArray()); + case Variant::PACKED_INT32_ARRAY: + return (MonoObject *)PackedInt32Array_to_mono_array(p_var.operator PackedInt32Array()); + case Variant::PACKED_INT64_ARRAY: + return (MonoObject *)PackedInt64Array_to_mono_array(p_var.operator PackedInt64Array()); + case Variant::PACKED_FLOAT32_ARRAY: + return (MonoObject *)PackedFloat32Array_to_mono_array(p_var.operator PackedFloat32Array()); + case Variant::PACKED_FLOAT64_ARRAY: + return (MonoObject *)PackedFloat64Array_to_mono_array(p_var.operator PackedFloat64Array()); + case Variant::PACKED_STRING_ARRAY: + return (MonoObject *)PackedStringArray_to_mono_array(p_var.operator PackedStringArray()); + case Variant::PACKED_VECTOR2_ARRAY: + return (MonoObject *)PackedVector2Array_to_mono_array(p_var.operator PackedVector2Array()); + case Variant::PACKED_VECTOR3_ARRAY: + return (MonoObject *)PackedVector3Array_to_mono_array(p_var.operator PackedVector3Array()); + case Variant::PACKED_COLOR_ARRAY: + return (MonoObject *)PackedColorArray_to_mono_array(p_var.operator PackedColorArray()); + default: + return nullptr; + } +} + +size_t variant_get_managed_unboxed_size(const ManagedType &p_type) { + // This method prints no errors for unsupported types. It's called on all methods, not only + // those that end up being invoked with Variant parameters. + + // For MonoObject* we return 0, as it doesn't need to be stored. + constexpr size_t zero_for_mono_object = 0; + + switch (p_type.type_encoding) { + case MONO_TYPE_BOOLEAN: + return sizeof(MonoBoolean); + case MONO_TYPE_CHAR: + return sizeof(uint16_t); + case MONO_TYPE_I1: + return sizeof(int8_t); + case MONO_TYPE_I2: + return sizeof(int16_t); + case MONO_TYPE_I4: + return sizeof(int32_t); + case MONO_TYPE_I8: + return sizeof(int64_t); + case MONO_TYPE_U1: + return sizeof(uint8_t); + case MONO_TYPE_U2: + return sizeof(uint16_t); + case MONO_TYPE_U4: + return sizeof(uint32_t); + case MONO_TYPE_U8: + return sizeof(uint64_t); + case MONO_TYPE_R4: + return sizeof(float); + case MONO_TYPE_R8: + return sizeof(double); + case MONO_TYPE_VALUETYPE: { + GDMonoClass *vtclass = p_type.type_class; + +#define RETURN_CHECK_FOR_STRUCT(m_struct) \ + if (vtclass == CACHED_CLASS(m_struct)) { \ + return sizeof(M_##m_struct); \ + } + + RETURN_CHECK_FOR_STRUCT(Vector2); + RETURN_CHECK_FOR_STRUCT(Vector2i); + RETURN_CHECK_FOR_STRUCT(Rect2); + RETURN_CHECK_FOR_STRUCT(Rect2i); + RETURN_CHECK_FOR_STRUCT(Transform2D); + RETURN_CHECK_FOR_STRUCT(Vector3); + RETURN_CHECK_FOR_STRUCT(Vector3i); + RETURN_CHECK_FOR_STRUCT(Basis); + RETURN_CHECK_FOR_STRUCT(Quat); + RETURN_CHECK_FOR_STRUCT(Transform); + RETURN_CHECK_FOR_STRUCT(AABB); + RETURN_CHECK_FOR_STRUCT(Color); + RETURN_CHECK_FOR_STRUCT(Plane); + RETURN_CHECK_FOR_STRUCT(Callable); + RETURN_CHECK_FOR_STRUCT(SignalInfo); + +#undef RETURN_CHECK_FOR_STRUCT + + if (mono_class_is_enum(vtclass->get_mono_ptr())) { + MonoType *enum_basetype = mono_class_enum_basetype(vtclass->get_mono_ptr()); + switch (mono_type_get_type(enum_basetype)) { + case MONO_TYPE_BOOLEAN: + return sizeof(MonoBoolean); + case MONO_TYPE_CHAR: + return sizeof(uint16_t); + case MONO_TYPE_I1: + return sizeof(int8_t); + case MONO_TYPE_I2: + return sizeof(int16_t); + case MONO_TYPE_I4: + return sizeof(int32_t); + case MONO_TYPE_I8: + return sizeof(int64_t); + case MONO_TYPE_U1: + return sizeof(uint8_t); + case MONO_TYPE_U2: + return sizeof(uint16_t); + case MONO_TYPE_U4: + return sizeof(uint32_t); + case MONO_TYPE_U8: + return sizeof(uint64_t); + default: { + // Enum with unsupported base type. We return nullptr MonoObject* on error. + return zero_for_mono_object; + } + } + } + + // Enum with unsupported value type. We return nullptr MonoObject* on error. + } break; + case MONO_TYPE_STRING: + return zero_for_mono_object; + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_CLASS: + case MONO_TYPE_GENERICINST: + return zero_for_mono_object; + case MONO_TYPE_OBJECT: + return zero_for_mono_object; + } + + // Unsupported type encoding. We return nullptr MonoObject* on error. + return zero_for_mono_object; +} + +void *variant_to_managed_unboxed(const Variant &p_var, const ManagedType &p_type, void *r_buffer, unsigned int &r_offset) { +#define RETURN_TYPE_VAL(m_type, m_val) \ + *reinterpret_cast<m_type *>(r_buffer) = m_val; \ + r_offset += sizeof(m_type); \ + return r_buffer; + + switch (p_type.type_encoding) { + case MONO_TYPE_BOOLEAN: + RETURN_TYPE_VAL(MonoBoolean, (MonoBoolean)p_var.operator bool()); + case MONO_TYPE_CHAR: + RETURN_TYPE_VAL(uint16_t, p_var.operator unsigned short()); + case MONO_TYPE_I1: + RETURN_TYPE_VAL(int8_t, p_var.operator signed char()); + case MONO_TYPE_I2: + RETURN_TYPE_VAL(int16_t, p_var.operator signed short()); + case MONO_TYPE_I4: + RETURN_TYPE_VAL(int32_t, p_var.operator signed int()); + case MONO_TYPE_I8: + RETURN_TYPE_VAL(int64_t, p_var.operator int64_t()); + case MONO_TYPE_U1: + RETURN_TYPE_VAL(uint8_t, p_var.operator unsigned char()); + case MONO_TYPE_U2: + RETURN_TYPE_VAL(uint16_t, p_var.operator unsigned short()); + case MONO_TYPE_U4: + RETURN_TYPE_VAL(uint32_t, p_var.operator unsigned int()); + case MONO_TYPE_U8: + RETURN_TYPE_VAL(uint64_t, p_var.operator uint64_t()); + case MONO_TYPE_R4: + RETURN_TYPE_VAL(float, p_var.operator float()); + case MONO_TYPE_R8: + RETURN_TYPE_VAL(double, p_var.operator double()); + case MONO_TYPE_VALUETYPE: { + GDMonoClass *vtclass = p_type.type_class; + +#define RETURN_CHECK_FOR_STRUCT(m_struct) \ + if (vtclass == CACHED_CLASS(m_struct)) { \ + GDMonoMarshal::M_##m_struct from = MARSHALLED_OUT(m_struct, p_var.operator ::m_struct()); \ + RETURN_TYPE_VAL(M_##m_struct, from); \ + } + + RETURN_CHECK_FOR_STRUCT(Vector2); + RETURN_CHECK_FOR_STRUCT(Vector2i); + RETURN_CHECK_FOR_STRUCT(Rect2); + RETURN_CHECK_FOR_STRUCT(Rect2i); + RETURN_CHECK_FOR_STRUCT(Transform2D); + RETURN_CHECK_FOR_STRUCT(Vector3); + RETURN_CHECK_FOR_STRUCT(Vector3i); + RETURN_CHECK_FOR_STRUCT(Basis); + RETURN_CHECK_FOR_STRUCT(Quat); + RETURN_CHECK_FOR_STRUCT(Transform); + RETURN_CHECK_FOR_STRUCT(AABB); + RETURN_CHECK_FOR_STRUCT(Color); + RETURN_CHECK_FOR_STRUCT(Plane); + +#undef RETURN_CHECK_FOR_STRUCT + + if (vtclass == CACHED_CLASS(Callable)) { + GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var.operator Callable()); + RETURN_TYPE_VAL(M_Callable, from); + } + + if (vtclass == CACHED_CLASS(SignalInfo)) { + GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var.operator Signal()); + RETURN_TYPE_VAL(M_SignalInfo, from); + } + + if (mono_class_is_enum(vtclass->get_mono_ptr())) { + MonoType *enum_basetype = mono_class_enum_basetype(vtclass->get_mono_ptr()); + switch (mono_type_get_type(enum_basetype)) { + case MONO_TYPE_BOOLEAN: { + MonoBoolean val = p_var.operator bool(); + RETURN_TYPE_VAL(MonoBoolean, val); + } + case MONO_TYPE_CHAR: { + uint16_t val = p_var.operator unsigned short(); + RETURN_TYPE_VAL(uint16_t, val); + } + case MONO_TYPE_I1: { + int8_t val = p_var.operator signed char(); + RETURN_TYPE_VAL(int8_t, val); + } + case MONO_TYPE_I2: { + int16_t val = p_var.operator signed short(); + RETURN_TYPE_VAL(int16_t, val); + } + case MONO_TYPE_I4: { + int32_t val = p_var.operator signed int(); + RETURN_TYPE_VAL(int32_t, val); + } + case MONO_TYPE_I8: { + int64_t val = p_var.operator int64_t(); + RETURN_TYPE_VAL(int64_t, val); + } + case MONO_TYPE_U1: { + uint8_t val = p_var.operator unsigned char(); + RETURN_TYPE_VAL(uint8_t, val); + } + case MONO_TYPE_U2: { + uint16_t val = p_var.operator unsigned short(); + RETURN_TYPE_VAL(uint16_t, val); + } + case MONO_TYPE_U4: { + uint32_t val = p_var.operator unsigned int(); + RETURN_TYPE_VAL(uint32_t, val); + } + case MONO_TYPE_U8: { + uint64_t val = p_var.operator uint64_t(); + RETURN_TYPE_VAL(uint64_t, val); + } + default: { + ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to enum value of unsupported base type: '" + + GDMonoClass::get_full_name(mono_class_from_mono_type(enum_basetype)) + "'."); + } + } + } + + ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported value type: '" + + p_type.type_class->get_full_name() + "'."); + } break; +#undef RETURN_TYPE_VAL + case MONO_TYPE_STRING: + return variant_to_mono_string(p_var); + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: + return variant_to_mono_array(p_var, p_type.type_class); + case MONO_TYPE_CLASS: + return variant_to_mono_object_of_class(p_var, p_type.type_class); + case MONO_TYPE_GENERICINST: + return variant_to_mono_object_of_genericinst(p_var, p_type.type_class); + case MONO_TYPE_OBJECT: + return variant_to_mono_object(p_var); + } + + ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported type with encoding: " + + itos(p_type.type_encoding) + "."); +} + +MonoObject *variant_to_mono_object(const Variant &p_var, const ManagedType &p_type) { switch (p_type.type_encoding) { case MONO_TYPE_BOOLEAN: { - MonoBoolean val = p_var->operator bool(); + MonoBoolean val = p_var.operator bool(); return BOX_BOOLEAN(val); } - case MONO_TYPE_CHAR: { - uint16_t val = p_var->operator unsigned short(); + uint16_t val = p_var.operator unsigned short(); return BOX_UINT16(val); } - case MONO_TYPE_I1: { - int8_t val = p_var->operator signed char(); + int8_t val = p_var.operator signed char(); return BOX_INT8(val); } case MONO_TYPE_I2: { - int16_t val = p_var->operator signed short(); + int16_t val = p_var.operator signed short(); return BOX_INT16(val); } case MONO_TYPE_I4: { - int32_t val = p_var->operator signed int(); + int32_t val = p_var.operator signed int(); return BOX_INT32(val); } case MONO_TYPE_I8: { - int64_t val = p_var->operator int64_t(); + int64_t val = p_var.operator int64_t(); return BOX_INT64(val); } - case MONO_TYPE_U1: { - uint8_t val = p_var->operator unsigned char(); + uint8_t val = p_var.operator unsigned char(); return BOX_UINT8(val); } case MONO_TYPE_U2: { - uint16_t val = p_var->operator unsigned short(); + uint16_t val = p_var.operator unsigned short(); return BOX_UINT16(val); } case MONO_TYPE_U4: { - uint32_t val = p_var->operator unsigned int(); + uint32_t val = p_var.operator unsigned int(); return BOX_UINT32(val); } case MONO_TYPE_U8: { - uint64_t val = p_var->operator uint64_t(); + uint64_t val = p_var.operator uint64_t(); return BOX_UINT64(val); } - case MONO_TYPE_R4: { - float val = p_var->operator float(); + float val = p_var.operator float(); return BOX_FLOAT(val); } case MONO_TYPE_R8: { - double val = p_var->operator double(); + double val = p_var.operator double(); return BOX_DOUBLE(val); } - - case MONO_TYPE_STRING: { - if (p_var->get_type() == Variant::NIL) { - return nullptr; // Otherwise, Variant -> String would return the string "Null" - } - return (MonoObject *)mono_string_from_godot(p_var->operator String()); - } break; - case MONO_TYPE_VALUETYPE: { GDMonoClass *vtclass = p_type.type_class; - if (vtclass == CACHED_CLASS(Vector2)) { - GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_var->operator ::Vector2()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2), &from); - } - - if (vtclass == CACHED_CLASS(Vector2i)) { - GDMonoMarshal::M_Vector2i from = MARSHALLED_OUT(Vector2i, p_var->operator ::Vector2i()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2i), &from); - } - - if (vtclass == CACHED_CLASS(Rect2)) { - GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_var->operator ::Rect2()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2), &from); - } - - if (vtclass == CACHED_CLASS(Rect2i)) { - GDMonoMarshal::M_Rect2i from = MARSHALLED_OUT(Rect2i, p_var->operator ::Rect2i()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2i), &from); - } - - if (vtclass == CACHED_CLASS(Transform2D)) { - GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_var->operator ::Transform2D()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform2D), &from); - } - - if (vtclass == CACHED_CLASS(Vector3)) { - GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_var->operator ::Vector3()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3), &from); - } - - if (vtclass == CACHED_CLASS(Vector3i)) { - GDMonoMarshal::M_Vector3i from = MARSHALLED_OUT(Vector3i, p_var->operator ::Vector3i()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3i), &from); - } - - if (vtclass == CACHED_CLASS(Basis)) { - GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_var->operator ::Basis()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Basis), &from); - } - - if (vtclass == CACHED_CLASS(Quat)) { - GDMonoMarshal::M_Quat from = MARSHALLED_OUT(Quat, p_var->operator ::Quat()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Quat), &from); - } - - if (vtclass == CACHED_CLASS(Transform)) { - GDMonoMarshal::M_Transform from = MARSHALLED_OUT(Transform, p_var->operator ::Transform()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform), &from); - } - - if (vtclass == CACHED_CLASS(AABB)) { - GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_var->operator ::AABB()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(AABB), &from); - } - - if (vtclass == CACHED_CLASS(Color)) { - GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_var->operator ::Color()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Color), &from); - } +#define RETURN_CHECK_FOR_STRUCT(m_struct) \ + if (vtclass == CACHED_CLASS(m_struct)) { \ + GDMonoMarshal::M_##m_struct from = MARSHALLED_OUT(m_struct, p_var.operator ::m_struct()); \ + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(m_struct), &from); \ + } - if (vtclass == CACHED_CLASS(Plane)) { - GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_var->operator ::Plane()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Plane), &from); - } + RETURN_CHECK_FOR_STRUCT(Vector2); + RETURN_CHECK_FOR_STRUCT(Vector2i); + RETURN_CHECK_FOR_STRUCT(Rect2); + RETURN_CHECK_FOR_STRUCT(Rect2i); + RETURN_CHECK_FOR_STRUCT(Transform2D); + RETURN_CHECK_FOR_STRUCT(Vector3); + RETURN_CHECK_FOR_STRUCT(Vector3i); + RETURN_CHECK_FOR_STRUCT(Basis); + RETURN_CHECK_FOR_STRUCT(Quat); + RETURN_CHECK_FOR_STRUCT(Transform); + RETURN_CHECK_FOR_STRUCT(AABB); + RETURN_CHECK_FOR_STRUCT(Color); + RETURN_CHECK_FOR_STRUCT(Plane); + +#undef RETURN_CHECK_FOR_STRUCT if (vtclass == CACHED_CLASS(Callable)) { - GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var->operator Callable()); + GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var.operator Callable()); return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Callable), &from); } if (vtclass == CACHED_CLASS(SignalInfo)) { - GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var->operator Signal()); + GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var.operator Signal()); return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(SignalInfo), &from); } @@ -465,316 +903,84 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty MonoClass *enum_baseclass = mono_class_from_mono_type(enum_basetype); switch (mono_type_get_type(enum_basetype)) { case MONO_TYPE_BOOLEAN: { - MonoBoolean val = p_var->operator bool(); + MonoBoolean val = p_var.operator bool(); return BOX_ENUM(enum_baseclass, val); } case MONO_TYPE_CHAR: { - uint16_t val = p_var->operator unsigned short(); + uint16_t val = p_var.operator unsigned short(); return BOX_ENUM(enum_baseclass, val); } case MONO_TYPE_I1: { - int8_t val = p_var->operator signed char(); + int8_t val = p_var.operator signed char(); return BOX_ENUM(enum_baseclass, val); } case MONO_TYPE_I2: { - int16_t val = p_var->operator signed short(); + int16_t val = p_var.operator signed short(); return BOX_ENUM(enum_baseclass, val); } case MONO_TYPE_I4: { - int32_t val = p_var->operator signed int(); + int32_t val = p_var.operator signed int(); return BOX_ENUM(enum_baseclass, val); } case MONO_TYPE_I8: { - int64_t val = p_var->operator int64_t(); + int64_t val = p_var.operator int64_t(); return BOX_ENUM(enum_baseclass, val); } case MONO_TYPE_U1: { - uint8_t val = p_var->operator unsigned char(); + uint8_t val = p_var.operator unsigned char(); return BOX_ENUM(enum_baseclass, val); } case MONO_TYPE_U2: { - uint16_t val = p_var->operator unsigned short(); + uint16_t val = p_var.operator unsigned short(); return BOX_ENUM(enum_baseclass, val); } case MONO_TYPE_U4: { - uint32_t val = p_var->operator unsigned int(); + uint32_t val = p_var.operator unsigned int(); return BOX_ENUM(enum_baseclass, val); } case MONO_TYPE_U8: { - uint64_t val = p_var->operator uint64_t(); + uint64_t val = p_var.operator uint64_t(); return BOX_ENUM(enum_baseclass, val); } default: { - ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to a managed enum value of unmarshallable base type."); + ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to enum value of unsupported base type: '" + + GDMonoClass::get_full_name(enum_baseclass) + "'."); } } } - } break; - - case MONO_TYPE_ARRAY: - case MONO_TYPE_SZARRAY: { - MonoArrayType *array_type = mono_type_get_array_type(p_type.type_class->get_mono_type()); - - if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) { - return (MonoObject *)Array_to_mono_array(p_var->operator Array()); - } - - if (array_type->eklass == CACHED_CLASS_RAW(uint8_t)) { - return (MonoObject *)PackedByteArray_to_mono_array(p_var->operator PackedByteArray()); - } - - if (array_type->eklass == CACHED_CLASS_RAW(int32_t)) { - return (MonoObject *)PackedInt32Array_to_mono_array(p_var->operator PackedInt32Array()); - } - - if (array_type->eklass == CACHED_CLASS_RAW(int64_t)) { - return (MonoObject *)PackedInt64Array_to_mono_array(p_var->operator PackedInt64Array()); - } - - if (array_type->eklass == CACHED_CLASS_RAW(float)) { - return (MonoObject *)PackedFloat32Array_to_mono_array(p_var->operator PackedFloat32Array()); - } - - if (array_type->eklass == CACHED_CLASS_RAW(double)) { - return (MonoObject *)PackedFloat64Array_to_mono_array(p_var->operator PackedFloat64Array()); - } - - if (array_type->eklass == CACHED_CLASS_RAW(String)) { - return (MonoObject *)PackedStringArray_to_mono_array(p_var->operator PackedStringArray()); - } - - if (array_type->eklass == CACHED_CLASS_RAW(Vector2)) { - return (MonoObject *)PackedVector2Array_to_mono_array(p_var->operator PackedVector2Array()); - } - - if (array_type->eklass == CACHED_CLASS_RAW(Vector3)) { - return (MonoObject *)PackedVector3Array_to_mono_array(p_var->operator PackedVector3Array()); - } - - if (array_type->eklass == CACHED_CLASS_RAW(Color)) { - return (MonoObject *)PackedColorArray_to_mono_array(p_var->operator PackedColorArray()); - } - - GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass); - if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class)) { - return (MonoObject *)Array_to_mono_array(p_var->operator Array(), array_type_class); - } - - ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to a managed array of unmarshallable element type."); - } break; - - case MONO_TYPE_CLASS: { - GDMonoClass *type_class = p_type.type_class; - - // GodotObject - if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) { - return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *()); - } - - if (CACHED_CLASS(StringName) == type_class) { - return GDMonoUtils::create_managed_from(p_var->operator StringName()); - } - - if (CACHED_CLASS(NodePath) == type_class) { - return GDMonoUtils::create_managed_from(p_var->operator NodePath()); - } - - if (CACHED_CLASS(RID) == type_class) { - return GDMonoUtils::create_managed_from(p_var->operator ::RID()); - } - - // Godot.Collections.Dictionary or IDictionary - if (CACHED_CLASS(Dictionary) == type_class || CACHED_CLASS(System_Collections_IDictionary) == type_class) { - return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary)); - } - - // Godot.Collections.Array or ICollection or IEnumerable - if (CACHED_CLASS(Array) == type_class || - CACHED_CLASS(System_Collections_ICollection) == type_class || - CACHED_CLASS(System_Collections_IEnumerable) == type_class) { - return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); - } - } break; - case MONO_TYPE_OBJECT: { - // Variant - switch (p_var->get_type()) { - case Variant::BOOL: { - MonoBoolean val = p_var->operator bool(); - return BOX_BOOLEAN(val); - } - case Variant::INT: { - int64_t val = p_var->operator int64_t(); - return BOX_INT64(val); - } - case Variant::FLOAT: { -#ifdef REAL_T_IS_DOUBLE - double val = p_var->operator double(); - return BOX_DOUBLE(val); -#else - float val = p_var->operator float(); - return BOX_FLOAT(val); -#endif - } - case Variant::STRING: - return (MonoObject *)mono_string_from_godot(p_var->operator String()); - case Variant::VECTOR2: { - GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_var->operator ::Vector2()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2), &from); - } - case Variant::VECTOR2I: { - GDMonoMarshal::M_Vector2i from = MARSHALLED_OUT(Vector2i, p_var->operator ::Vector2i()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2i), &from); - } - case Variant::RECT2: { - GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_var->operator ::Rect2()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2), &from); - } - case Variant::RECT2I: { - GDMonoMarshal::M_Rect2i from = MARSHALLED_OUT(Rect2i, p_var->operator ::Rect2i()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2i), &from); - } - case Variant::VECTOR3: { - GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_var->operator ::Vector3()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3), &from); - } - case Variant::VECTOR3I: { - GDMonoMarshal::M_Vector3i from = MARSHALLED_OUT(Vector3i, p_var->operator ::Vector3i()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3i), &from); - } - case Variant::TRANSFORM2D: { - GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_var->operator ::Transform2D()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform2D), &from); - } - case Variant::PLANE: { - GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_var->operator ::Plane()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Plane), &from); - } - case Variant::QUAT: { - GDMonoMarshal::M_Quat from = MARSHALLED_OUT(Quat, p_var->operator ::Quat()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Quat), &from); - } - case Variant::AABB: { - GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_var->operator ::AABB()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(AABB), &from); - } - case Variant::BASIS: { - GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_var->operator ::Basis()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Basis), &from); - } - case Variant::TRANSFORM: { - GDMonoMarshal::M_Transform from = MARSHALLED_OUT(Transform, p_var->operator ::Transform()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform), &from); - } - case Variant::COLOR: { - GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_var->operator ::Color()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Color), &from); - } - case Variant::STRING_NAME: - return GDMonoUtils::create_managed_from(p_var->operator StringName()); - case Variant::NODE_PATH: - return GDMonoUtils::create_managed_from(p_var->operator NodePath()); - case Variant::RID: - return GDMonoUtils::create_managed_from(p_var->operator ::RID()); - case Variant::OBJECT: - return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *()); - case Variant::CALLABLE: { - GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var->operator Callable()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Callable), &from); - } - case Variant::SIGNAL: { - GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var->operator Signal()); - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(SignalInfo), &from); - } - case Variant::DICTIONARY: - return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary)); - case Variant::ARRAY: - return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); - case Variant::PACKED_BYTE_ARRAY: - return (MonoObject *)PackedByteArray_to_mono_array(p_var->operator PackedByteArray()); - case Variant::PACKED_INT32_ARRAY: - return (MonoObject *)PackedInt32Array_to_mono_array(p_var->operator PackedInt32Array()); - case Variant::PACKED_INT64_ARRAY: - return (MonoObject *)PackedInt64Array_to_mono_array(p_var->operator PackedInt64Array()); - case Variant::PACKED_FLOAT32_ARRAY: - return (MonoObject *)PackedFloat32Array_to_mono_array(p_var->operator PackedFloat32Array()); - case Variant::PACKED_FLOAT64_ARRAY: - return (MonoObject *)PackedFloat64Array_to_mono_array(p_var->operator PackedFloat64Array()); - case Variant::PACKED_STRING_ARRAY: - return (MonoObject *)PackedStringArray_to_mono_array(p_var->operator PackedStringArray()); - case Variant::PACKED_VECTOR2_ARRAY: - return (MonoObject *)PackedVector2Array_to_mono_array(p_var->operator PackedVector2Array()); - case Variant::PACKED_VECTOR3_ARRAY: - return (MonoObject *)PackedVector3Array_to_mono_array(p_var->operator PackedVector3Array()); - case Variant::PACKED_COLOR_ARRAY: - return (MonoObject *)PackedColorArray_to_mono_array(p_var->operator PackedColorArray()); - default: - return nullptr; - } - break; - case MONO_TYPE_GENERICINST: { - MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type()); - - // Godot.Collections.Dictionary<TKey, TValue> - if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) { - return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), p_type.type_class); - } - - // Godot.Collections.Array<T> - if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) { - return GDMonoUtils::create_managed_from(p_var->operator Array(), p_type.type_class); - } - - // System.Collections.Generic.Dictionary<TKey, TValue> - if (GDMonoUtils::Marshal::type_is_system_generic_dictionary(reftype)) { - MonoReflectionType *key_reftype = nullptr; - MonoReflectionType *value_reftype = nullptr; - GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype); - return Dictionary_to_system_generic_dict(p_var->operator Dictionary(), p_type.type_class, key_reftype, value_reftype); - } - - // System.Collections.Generic.List<T> - if (GDMonoUtils::Marshal::type_is_system_generic_list(reftype)) { - MonoReflectionType *elem_reftype = nullptr; - GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype); - return Array_to_system_generic_list(p_var->operator Array(), p_type.type_class, elem_reftype); - } - - // IDictionary<TKey, TValue> - if (GDMonoUtils::Marshal::type_is_generic_idictionary(reftype)) { - MonoReflectionType *key_reftype; - MonoReflectionType *value_reftype; - GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype); - GDMonoClass *godot_dict_class = GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype); - - return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), godot_dict_class); - } - // ICollection<T> or IEnumerable<T> - if (GDMonoUtils::Marshal::type_is_generic_icollection(reftype) || GDMonoUtils::Marshal::type_is_generic_ienumerable(reftype)) { - MonoReflectionType *elem_reftype; - GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype); - GDMonoClass *godot_array_class = GDMonoUtils::Marshal::make_generic_array_type(elem_reftype); - - return GDMonoUtils::create_managed_from(p_var->operator Array(), godot_array_class); - } - } break; + ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported value type: '" + + p_type.type_class->get_full_name() + "'."); } break; + case MONO_TYPE_STRING: + return (MonoObject *)variant_to_mono_string(p_var); + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: + return (MonoObject *)variant_to_mono_array(p_var, p_type.type_class); + case MONO_TYPE_CLASS: + return variant_to_mono_object_of_class(p_var, p_type.type_class); + case MONO_TYPE_GENERICINST: + return variant_to_mono_object_of_genericinst(p_var, p_type.type_class); + case MONO_TYPE_OBJECT: + return variant_to_mono_object(p_var); } - ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to an unmarshallable managed type. Name: '" + - p_type.type_class->get_name() + "' Encoding: " + itos(p_type.type_encoding) + "."); + ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported type with encoding: " + + itos(p_type.type_encoding) + "."); } Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type, bool p_fail_with_err = true) { ERR_FAIL_COND_V(!p_type.type_class, Variant()); +#ifdef DEBUG_ENABLED + CRASH_COND_MSG(p_type.type_encoding == MONO_TYPE_OBJECT, "Type of object should be known."); +#endif + switch (p_type.type_encoding) { case MONO_TYPE_BOOLEAN: return (bool)unbox<MonoBoolean>(p_obj); - case MONO_TYPE_CHAR: return unbox<uint16_t>(p_obj); - case MONO_TYPE_I1: return unbox<int8_t>(p_obj); case MONO_TYPE_I2: @@ -783,7 +989,6 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type return unbox<int32_t>(p_obj); case MONO_TYPE_I8: return unbox<int64_t>(p_obj); - case MONO_TYPE_U1: return unbox<uint8_t>(p_obj); case MONO_TYPE_U2: @@ -792,19 +997,10 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type return unbox<uint32_t>(p_obj); case MONO_TYPE_U8: return unbox<uint64_t>(p_obj); - case MONO_TYPE_R4: return unbox<float>(p_obj); case MONO_TYPE_R8: return unbox<double>(p_obj); - - case MONO_TYPE_STRING: { - if (p_obj == nullptr) { - return Variant(); // NIL - } - return mono_string_to_godot_not_null((MonoString *)p_obj); - } break; - case MONO_TYPE_VALUETYPE: { GDMonoClass *vtclass = p_type.type_class; @@ -872,7 +1068,12 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type return unbox<int32_t>(p_obj); } } break; - + case MONO_TYPE_STRING: { + if (p_obj == nullptr) { + return Variant(); // NIL + } + return mono_string_to_godot_not_null((MonoString *)p_obj); + } break; case MONO_TYPE_ARRAY: case MONO_TYPE_SZARRAY: { MonoArrayType *array_type = mono_type_get_array_type(p_type.type_class->get_mono_type()); @@ -928,7 +1129,6 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type return Variant(); } } break; - case MONO_TYPE_CLASS: { GDMonoClass *type_class = p_type.type_class; @@ -973,7 +1173,6 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type return ptr ? Variant(*ptr) : Variant(); } } break; - case MONO_TYPE_GENERICINST: { MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type()); @@ -1052,7 +1251,7 @@ String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc) { ManagedType type = ManagedType::from_class(mono_object_get_class(p_obj)); Variant var = GDMonoMarshal::mono_object_to_variant_no_err(p_obj, type); - if (var.get_type() == Variant::NIL && p_obj != nullptr) { + if (var.get_type() == Variant::NIL) { // `&& p_obj != nullptr` but omitted because always true // Cannot convert MonoObject* to Variant; fallback to 'ToString()'. MonoException *exc = nullptr; MonoString *mono_str = GDMonoUtils::object_to_string(p_obj, &exc); @@ -1115,9 +1314,10 @@ Dictionary system_generic_dict_to_Dictionary(MonoObject *p_obj, [[maybe_unused]] } MonoObject *Array_to_system_generic_list(const Array &p_array, GDMonoClass *p_class, MonoReflectionType *p_elem_reftype) { - GDMonoClass *elem_class = ManagedType::from_reftype(p_elem_reftype).type_class; + MonoType *elem_type = mono_reflection_type_get_type(p_elem_reftype); + MonoClass *elem_class = mono_class_from_mono_type(elem_type); - String ctor_desc = ":.ctor(System.Collections.Generic.IEnumerable`1<" + elem_class->get_type_desc() + ">)"; + String ctor_desc = ":.ctor(System.Collections.Generic.IEnumerable`1<" + GDMonoUtils::get_type_desc(elem_type) + ">)"; GDMonoMethod *ctor = p_class->get_method_with_desc(ctor_desc, true); CRASH_COND(ctor == nullptr); @@ -1156,9 +1356,9 @@ MonoArray *Array_to_mono_array(const Array &p_array) { return ret; } -MonoArray *Array_to_mono_array(const Array &p_array, GDMonoClass *p_array_type_class) { +MonoArray *Array_to_mono_array(const Array &p_array, MonoClass *p_array_type_class) { int length = p_array.size(); - MonoArray *ret = mono_array_new(mono_domain_get(), p_array_type_class->get_mono_ptr(), length); + MonoArray *ret = mono_array_new(mono_domain_get(), p_array_type_class, length); for (int i = 0; i < length; i++) { MonoObject *boxed = variant_to_mono_object(p_array[i]); diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h index d1d5f1f202..7d0036a1d8 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.h +++ b/modules/mono/mono_gd/gd_mono_marshal.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -90,15 +90,40 @@ _FORCE_INLINE_ MonoString *mono_string_from_godot(const String &p_string) { // Variant -MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_type); -MonoObject *variant_to_mono_object(const Variant *p_var); +size_t variant_get_managed_unboxed_size(const ManagedType &p_type); +void *variant_to_managed_unboxed(const Variant &p_var, const ManagedType &p_type, void *r_buffer, unsigned int &r_offset); +MonoObject *variant_to_mono_object(const Variant &p_var, const ManagedType &p_type); -_FORCE_INLINE_ MonoObject *variant_to_mono_object(const Variant &p_var) { - return variant_to_mono_object(&p_var); -} +MonoObject *variant_to_mono_object(const Variant &p_var); +MonoArray *variant_to_mono_array(const Variant &p_var, GDMonoClass *p_type_class); +MonoObject *variant_to_mono_object_of_class(const Variant &p_var, GDMonoClass *p_type_class); +MonoObject *variant_to_mono_object_of_genericinst(const Variant &p_var, GDMonoClass *p_type_class); +MonoString *variant_to_mono_string(const Variant &p_var); + +// These overloads were added to avoid passing a `const Variant *` to the `const Variant &` +// parameter. That would result in the `Variant(bool)` copy constructor being called as +// pointers are implicitly converted to bool. Implicit conversions are f-ing evil. -_FORCE_INLINE_ MonoObject *variant_to_mono_object(const Variant &p_var, const ManagedType &p_type) { - return variant_to_mono_object(&p_var, p_type); +_FORCE_INLINE_ void *variant_to_managed_unboxed(const Variant *p_var, const ManagedType &p_type, void *r_buffer, unsigned int &r_offset) { + return variant_to_managed_unboxed(*p_var, p_type, r_buffer, r_offset); +} +_FORCE_INLINE_ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_type) { + return variant_to_mono_object(*p_var, p_type); +} +_FORCE_INLINE_ MonoObject *variant_to_mono_object(const Variant *p_var) { + return variant_to_mono_object(*p_var); +} +_FORCE_INLINE_ MonoArray *variant_to_mono_array(const Variant *p_var, GDMonoClass *p_type_class) { + return variant_to_mono_array(*p_var, p_type_class); +} +_FORCE_INLINE_ MonoObject *variant_to_mono_object_of_class(const Variant *p_var, GDMonoClass *p_type_class) { + return variant_to_mono_object_of_class(*p_var, p_type_class); +} +_FORCE_INLINE_ MonoObject *variant_to_mono_object_of_genericinst(const Variant *p_var, GDMonoClass *p_type_class) { + return variant_to_mono_object_of_genericinst(*p_var, p_type_class); +} +_FORCE_INLINE_ MonoString *variant_to_mono_string(const Variant *p_var) { + return variant_to_mono_string(*p_var); } Variant mono_object_to_variant(MonoObject *p_obj); @@ -120,7 +145,7 @@ Array system_generic_list_to_Array(MonoObject *p_obj, GDMonoClass *p_class, Mono // Array MonoArray *Array_to_mono_array(const Array &p_array); -MonoArray *Array_to_mono_array(const Array &p_array, GDMonoClass *p_array_type_class); +MonoArray *Array_to_mono_array(const Array &p_array, MonoClass *p_array_type_class); Array mono_array_to_Array(MonoArray *p_array); // PackedInt32Array diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp index 04f3b25a70..67aabcde10 100644 --- a/modules/mono/mono_gd/gd_mono_method.cpp +++ b/modules/mono/mono_gd/gd_mono_method.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -75,6 +75,10 @@ void GDMonoMethod::_update_signature(MonoMethodSignature *p_method_sig) { // clear the cache method_info_fetched = false; method_info = MethodInfo(); + + for (int i = 0; i < params_count; i++) { + params_buffer_size += GDMonoMarshal::variant_get_managed_unboxed_size(param_types[i]); + } } GDMonoClass *GDMonoMethod::get_enclosing_class() const { @@ -107,14 +111,15 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoObject *ret; if (params_count > 0) { - MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), params_count); + void **params = (void **)alloca(params_count * sizeof(void *)); + uint8_t *buffer = (uint8_t *)alloca(params_buffer_size); + unsigned int offset = 0; for (int i = 0; i < params_count; i++) { - MonoObject *boxed_param = GDMonoMarshal::variant_to_mono_object(p_params[i], param_types[i]); - mono_array_setref(params, i, boxed_param); + params[i] = GDMonoMarshal::variant_to_managed_unboxed(p_params[i], param_types[i], buffer + offset, offset); } - ret = GDMonoUtils::runtime_invoke_array(mono_method, p_object, params, &exc); + ret = GDMonoUtils::runtime_invoke(mono_method, p_object, params, &exc); } else { ret = GDMonoUtils::runtime_invoke(mono_method, p_object, nullptr, &exc); } @@ -279,16 +284,8 @@ const MethodInfo &GDMonoMethod::get_method_info() { return method_info; } -GDMonoMethod::GDMonoMethod(StringName p_name, MonoMethod *p_method) { - name = p_name; - - mono_method = p_method; - - method_info_fetched = false; - - attrs_fetched = false; - attributes = nullptr; - +GDMonoMethod::GDMonoMethod(StringName p_name, MonoMethod *p_method) : + name(p_name), mono_method(p_method) { _update_signature(); } diff --git a/modules/mono/mono_gd/gd_mono_method.h b/modules/mono/mono_gd/gd_mono_method.h index f78f57dca0..c08ffe904b 100644 --- a/modules/mono/mono_gd/gd_mono_method.h +++ b/modules/mono/mono_gd/gd_mono_method.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -38,15 +38,16 @@ class GDMonoMethod : public IMonoClassMember { StringName name; - int params_count; + uint16_t params_count; + unsigned int params_buffer_size = 0; ManagedType return_type; Vector<ManagedType> param_types; - bool method_info_fetched; + bool method_info_fetched = false; MethodInfo method_info; - bool attrs_fetched; - MonoCustomAttrInfo *attributes; + bool attrs_fetched = false; + MonoCustomAttrInfo *attributes = nullptr; void _update_signature(); void _update_signature(MonoMethodSignature *p_method_sig); @@ -72,7 +73,7 @@ public: _FORCE_INLINE_ MonoMethod *get_mono_ptr() const { return mono_method; } - _FORCE_INLINE_ int get_parameters_count() const { return params_count; } + _FORCE_INLINE_ uint16_t get_parameters_count() const { return params_count; } _FORCE_INLINE_ ManagedType get_return_type() const { return return_type; } MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc = nullptr) const; diff --git a/modules/mono/mono_gd/gd_mono_method_thunk.h b/modules/mono/mono_gd/gd_mono_method_thunk.h index 01f3ae342a..091d26df1d 100644 --- a/modules/mono/mono_gd/gd_mono_method_thunk.h +++ b/modules/mono/mono_gd/gd_mono_method_thunk.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp index bc3be97102..dc3d225082 100644 --- a/modules/mono/mono_gd/gd_mono_property.cpp +++ b/modules/mono/mono_gd/gd_mono_property.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -149,10 +149,9 @@ bool GDMonoProperty::has_setter() { void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc) { MonoMethod *prop_method = mono_property_get_set_method(mono_property); - MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), 1); - mono_array_setref(params, 0, p_value); + void *params[1] = { p_value }; MonoException *exc = nullptr; - GDMonoUtils::runtime_invoke_array(prop_method, p_object, params, &exc); + GDMonoUtils::runtime_invoke(prop_method, p_object, params, &exc); if (exc) { if (r_exc) { *r_exc = exc; diff --git a/modules/mono/mono_gd/gd_mono_property.h b/modules/mono/mono_gd/gd_mono_property.h index 611ac293e4..af7a2c02e5 100644 --- a/modules/mono/mono_gd/gd_mono_property.h +++ b/modules/mono/mono_gd/gd_mono_property.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index 97fc4c57f9..6e0a263c7f 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -480,6 +480,7 @@ void set_pending_exception(MonoException *p_exc) { #else if (get_runtime_invoke_count() == 0) { debug_unhandled_exception(p_exc); + return; } if (!mono_runtime_set_pending_exception(p_exc, false)) { @@ -498,13 +499,6 @@ MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, M return ret; } -MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **r_exc) { - GD_MONO_BEGIN_RUNTIME_INVOKE; - MonoObject *ret = mono_runtime_invoke_array(p_method, p_obj, p_params, (MonoObject **)r_exc); - GD_MONO_END_RUNTIME_INVOKE; - return ret; -} - MonoString *object_to_string(MonoObject *p_obj, MonoException **r_exc) { GD_MONO_BEGIN_RUNTIME_INVOKE; MonoString *ret = mono_object_to_string(p_obj, (MonoObject **)r_exc); diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index 71c131f77c..9e024418e1 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,6 +36,9 @@ #include "../mono_gc_handle.h" #include "../utils/macros.h" #include "gd_mono_header.h" +#ifdef JAVASCRIPT_ENABLED +#include "gd_mono_wasm_m2n.h" +#endif #include "core/object/class_db.h" #include "core/object/reference.h" @@ -135,7 +138,6 @@ _FORCE_INLINE_ int &get_runtime_invoke_count_ref() { } MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **r_exc); -MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **r_exc); MonoString *object_to_string(MonoObject *p_obj, MonoException **r_exc); @@ -155,6 +157,22 @@ private: }; StringName get_native_godot_class_name(GDMonoClass *p_class); + +template <typename... P> +void add_internal_call(const char *p_name, void (*p_func)(P...)) { +#ifdef JAVASCRIPT_ENABLED + GDMonoWasmM2n::ICallTrampolines<P...>::add(); +#endif + mono_add_internal_call(p_name, (void *)p_func); +} + +template <typename R, typename... P> +void add_internal_call(const char *p_name, R (*p_func)(P...)) { +#ifdef JAVASCRIPT_ENABLED + GDMonoWasmM2n::ICallTrampolinesR<R, P...>::add(); +#endif + mono_add_internal_call(p_name, (void *)p_func); +} } // namespace GDMonoUtils #define NATIVE_GDMONOCLASS_NAME(m_class) (GDMonoUtils::get_native_godot_class_name(m_class)) diff --git a/modules/mono/mono_gd/gd_mono_wasm_m2n.cpp b/modules/mono/mono_gd/gd_mono_wasm_m2n.cpp new file mode 100644 index 0000000000..a477c55456 --- /dev/null +++ b/modules/mono/mono_gd/gd_mono_wasm_m2n.cpp @@ -0,0 +1,79 @@ +/*************************************************************************/ +/* gd_mono_wasm_m2n.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "gd_mono_wasm_m2n.h" + +#ifdef JAVASCRIPT_ENABLED + +#include "core/templates/oa_hash_map.h" + +typedef mono_bool (*GodotMonoM2nIcallTrampolineDispatch)(const char *cookie, void *target_func, Mono_InterpMethodArguments *margs); + +// This extern function is implemented in our patched version of Mono +MONO_API void godot_mono_register_m2n_icall_trampoline_dispatch_hook(GodotMonoM2nIcallTrampolineDispatch hook); + +namespace GDMonoWasmM2n { + +struct HashMapCookieComparator { + static bool compare(const char *p_lhs, const char *p_rhs) { + return strcmp(p_lhs, p_rhs) == 0; + } +}; + +// The default hasher supports 'const char *' C Strings, but we need a custom comparator +OAHashMap<const char *, TrampolineFunc, HashMapHasherDefault, HashMapCookieComparator> trampolines; + +void set_trampoline(const char *cookies, GDMonoWasmM2n::TrampolineFunc trampoline_func) { + trampolines.set(cookies, trampoline_func); +} + +mono_bool trampoline_dispatch_hook(const char *cookie, void *target_func, Mono_InterpMethodArguments *margs) { + TrampolineFunc *trampoline_func = trampolines.lookup_ptr(cookie); + + if (!trampoline_func) { + return false; + } + + (*trampoline_func)(target_func, margs); + return true; +} + +bool initialized = false; + +void lazy_initialize() { + // Doesn't need to be thread safe + if (!initialized) { + initialized = true; + godot_mono_register_m2n_icall_trampoline_dispatch_hook(&trampoline_dispatch_hook); + } +} +} // namespace GDMonoWasmM2n + +#endif diff --git a/modules/mono/mono_gd/gd_mono_wasm_m2n.h b/modules/mono/mono_gd/gd_mono_wasm_m2n.h new file mode 100644 index 0000000000..159a2ed7b6 --- /dev/null +++ b/modules/mono/mono_gd/gd_mono_wasm_m2n.h @@ -0,0 +1,263 @@ +/*************************************************************************/ +/* gd_mono_wasm_m2n.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef GD_MONO_WASM_M2N_H +#define GD_MONO_WASM_M2N_H + +#ifdef JAVASCRIPT_ENABLED + +#include "core/string/ustring.h" +#include "core/typedefs.h" + +#include <mono/metadata/loader.h> +#include <mono/utils/mono-publib.h> +#include <stdexcept> +#include <type_traits> + +extern "C" { + +struct Mono_InterpMethodArguments { + size_t ilen; + void **iargs; + size_t flen; + double *fargs; + void **retval; + size_t is_float_ret; + //#ifdef TARGET_WASM + void *sig; + //#endif +}; +} // extern "C" + +namespace GDMonoWasmM2n { + +template <typename T, size_t Size> +struct array { + T elems[Size]; +}; + +template <typename T> +constexpr char get_m2n_cookie_impl() { +#define M2N_REG_COOKIE(m_type, m_cookie) \ + if constexpr (std::is_same_v<m_type, T>) { \ + return m_cookie; \ + } + + M2N_REG_COOKIE(MonoBoolean, 'I'); + M2N_REG_COOKIE(int8_t, 'I'); + M2N_REG_COOKIE(uint8_t, 'I'); + M2N_REG_COOKIE(int16_t, 'I'); + M2N_REG_COOKIE(uint16_t, 'I'); + M2N_REG_COOKIE(int32_t, 'I'); + M2N_REG_COOKIE(uint32_t, 'I'); + M2N_REG_COOKIE(int64_t, 'L'); + M2N_REG_COOKIE(uint64_t, 'L'); + M2N_REG_COOKIE(float, 'F'); + M2N_REG_COOKIE(double, 'D'); + + if constexpr (std::is_pointer_v<T>) { + if constexpr (sizeof(void *) == 4) { + return 'I'; + } else { + return 'L'; + } + } + + if constexpr (std::is_void_v<T>) { + return 'V'; + } + + return 'X'; + +#undef M2N_REG_COOKIE +} + +template <typename T> +constexpr char get_m2n_cookie() { + constexpr char cookie = get_m2n_cookie_impl<T>(); + static_assert(cookie != 'X', "Type not supported in internal call signature."); + return cookie; +} + +template <typename... T> +constexpr array<const char, sizeof...(T) + 2> get_m2n_cookies() { + return array<const char, sizeof...(T) + 2>{ 'V', get_m2n_cookie<T>()..., '\0' }; +} + +template <typename R, typename... T> +constexpr array<const char, sizeof...(T) + 2> get_m2n_cookies_r() { + return array<const char, sizeof...(T) + 2>{ get_m2n_cookie<R>(), get_m2n_cookie<T>()..., '\0' }; +} + +template <typename T> +constexpr size_t calc_m2n_index(size_t &r_int_idx, size_t &r_float_idx) { + constexpr char cookie = get_m2n_cookie<T>(); + + static_assert(cookie == 'I' || cookie == 'L' || cookie == 'F' || cookie == 'D'); + + if constexpr (cookie == 'I' || cookie == 'L') { + size_t ret = r_int_idx; + r_int_idx += cookie == 'I' ? 1 : 2; + return ret; + } else { + size_t ret = r_float_idx; + r_float_idx += cookie == 'F' ? 1 : 2; + return ret; + } +} + +template <typename... P> +constexpr array<size_t, sizeof...(P)> get_indices_for_type() { + size_t int_idx = 0; + size_t float_idx = 0; + return array<size_t, sizeof...(P)>{ calc_m2n_index<P>(int_idx, float_idx)... }; +} + +constexpr size_t fidx(size_t p_x) { + if constexpr (sizeof(void *) == 4) { + return p_x * 2; + } else { + return p_x; + } +} + +template <typename T> +T m2n_arg_cast(Mono_InterpMethodArguments *p_margs, size_t p_idx) { + constexpr char cookie = get_m2n_cookie<T>(); + + static_assert(cookie == 'I' || cookie == 'L' || cookie == 'F' || cookie == 'D'); + + if constexpr (cookie == 'I') { + return (T)(size_t)p_margs->iargs[p_idx]; + } else if constexpr (cookie == 'L') { + static_assert(std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t> || + (sizeof(void *) == 8 && std::is_pointer_v<T>), + "Invalid type for cookie 'L'."); + + union { + T l; + struct { + int32_t lo; + int32_t hi; + } pair; + } p; + + p.pair.lo = (int32_t)(size_t)p_margs->iargs[p_idx]; + p.pair.hi = (int32_t)(size_t)p_margs->iargs[p_idx + 1]; + + return p.l; + } else if constexpr (cookie == 'F') { + return *reinterpret_cast<float *>(&p_margs->fargs[fidx(p_idx)]); + } else if constexpr (cookie == 'D') { + return (T)(size_t)p_margs->fargs[p_idx]; + } +} + +template <typename... P, size_t... Is> +void m2n_trampoline_with_idx_seq(void *p_target_func, Mono_InterpMethodArguments *p_margs, IndexSequence<Is...>) { + constexpr array<size_t, sizeof...(P)> indices = get_indices_for_type<P...>(); + typedef void (*Func)(P...); + Func func = (Func)p_target_func; + func(m2n_arg_cast<P>(p_margs, indices.elems[Is])...); +} + +template <typename R, typename... P, size_t... Is> +void m2n_trampoline_with_idx_seq_r(void *p_target_func, Mono_InterpMethodArguments *p_margs, IndexSequence<Is...>) { + constexpr array<size_t, sizeof...(P)> indices = get_indices_for_type<P...>(); + typedef R (*Func)(P...); + Func func = (Func)p_target_func; + R res = func(m2n_arg_cast<P>(p_margs, indices.elems[Is])...); + *reinterpret_cast<R *>(p_margs->retval) = res; +} + +inline void m2n_trampoline_with_idx_seq_0(void *p_target_func, Mono_InterpMethodArguments *p_margs) { + typedef void (*Func)(); + Func func = (Func)p_target_func; + func(); +} + +template <typename R> +void m2n_trampoline_with_idx_seq_r0(void *p_target_func, Mono_InterpMethodArguments *p_margs) { + typedef R (*Func)(); + Func func = (Func)p_target_func; + R res = func(); + *reinterpret_cast<R *>(p_margs->retval) = res; +} + +template <typename... P> +void m2n_trampoline(void *p_target_func, Mono_InterpMethodArguments *p_margs) { + if constexpr (sizeof...(P) == 0) { + m2n_trampoline_with_idx_seq_0(p_target_func, p_margs); + } else { + m2n_trampoline_with_idx_seq<P...>(p_target_func, p_margs, BuildIndexSequence<sizeof...(P)>{}); + } +} + +template <typename R, typename... P> +void m2n_trampoline_r(void *p_target_func, Mono_InterpMethodArguments *p_margs) { + if constexpr (sizeof...(P) == 0) { + m2n_trampoline_with_idx_seq_r0<R>(p_target_func, p_margs); + } else { + m2n_trampoline_with_idx_seq_r<R, P...>(p_target_func, p_margs, BuildIndexSequence<sizeof...(P)>{}); + } +} + +typedef void (*TrampolineFunc)(void *p_target_func, Mono_InterpMethodArguments *p_margs); + +void set_trampoline(const char *cookies, TrampolineFunc trampoline_func); + +void lazy_initialize(); + +template <typename... P> +struct ICallTrampolines { + static constexpr auto cookies = get_m2n_cookies<P...>(); + + static void add() { + lazy_initialize(); + set_trampoline(cookies.elems, &m2n_trampoline<P...>); + } +}; + +template <typename R, typename... P> +struct ICallTrampolinesR { + static constexpr auto cookies = get_m2n_cookies_r<R, P...>(); + + static void add() { + lazy_initialize(); + set_trampoline(cookies.elems, &m2n_trampoline_r<R, P...>); + } +}; + +void initialize(); +} // namespace GDMonoWasmM2n + +#endif + +#endif // GD_MONO_WASM_M2N_H diff --git a/modules/mono/mono_gd/i_mono_class_member.h b/modules/mono/mono_gd/i_mono_class_member.h index 2e8e01c80e..36e14ba27c 100644 --- a/modules/mono/mono_gd/i_mono_class_member.h +++ b/modules/mono/mono_gd/i_mono_class_member.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/mono_gd/managed_type.cpp b/modules/mono/mono_gd/managed_type.cpp index 3e971efece..0acfafe841 100644 --- a/modules/mono/mono_gd/managed_type.cpp +++ b/modules/mono/mono_gd/managed_type.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/mono_gd/managed_type.h b/modules/mono/mono_gd/managed_type.h index 491a2f3d20..0456a9a864 100644 --- a/modules/mono/mono_gd/managed_type.h +++ b/modules/mono/mono_gd/managed_type.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/mono_gd/support/android_support.cpp b/modules/mono/mono_gd/support/android_support.cpp index 18daf859b5..59e1385e7e 100644 --- a/modules/mono/mono_gd/support/android_support.cpp +++ b/modules/mono/mono_gd/support/android_support.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -134,7 +134,7 @@ String determine_app_native_lib_dir() { } String get_app_native_lib_dir() { - if (app_native_lib_dir_cache.empty()) + if (app_native_lib_dir_cache.is_empty()) app_native_lib_dir_cache = determine_app_native_lib_dir(); return app_native_lib_dir_cache; } @@ -355,8 +355,8 @@ MonoArray *_gd_mono_android_cert_store_lookup(MonoString *p_alias) { } void register_internal_calls() { - mono_add_internal_call("Android.Runtime.AndroidEnvironment::_gd_mono_init_cert_store", (void *)_gd_mono_init_cert_store); - mono_add_internal_call("Android.Runtime.AndroidEnvironment::_gd_mono_android_cert_store_lookup", (void *)_gd_mono_android_cert_store_lookup); + GDMonoUtils::add_internal_call("Android.Runtime.AndroidEnvironment::_gd_mono_init_cert_store", _gd_mono_init_cert_store); + GDMonoUtils::add_internal_call("Android.Runtime.AndroidEnvironment::_gd_mono_android_cert_store_lookup", _gd_mono_android_cert_store_lookup); } void initialize() { diff --git a/modules/mono/mono_gd/support/android_support.h b/modules/mono/mono_gd/support/android_support.h index df51100bef..0c5dd2764c 100755 --- a/modules/mono/mono_gd/support/android_support.h +++ b/modules/mono/mono_gd/support/android_support.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/mono_gd/support/ios_support.h b/modules/mono/mono_gd/support/ios_support.h index 48cef890d6..28a8806d0e 100755 --- a/modules/mono/mono_gd/support/ios_support.h +++ b/modules/mono/mono_gd/support/ios_support.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/mono_gd/support/ios_support.mm b/modules/mono/mono_gd/support/ios_support.mm index e6e09c4146..cdee04edcf 100644 --- a/modules/mono/mono_gd/support/ios_support.mm +++ b/modules/mono/mono_gd/support/ios_support.mm @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/register_types.cpp b/modules/mono/register_types.cpp index f5c1bda18b..80eb47bfd4 100644 --- a/modules/mono/register_types.cpp +++ b/modules/mono/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/register_types.h b/modules/mono/register_types.h index e30d9a8abd..1a2ff004b5 100644 --- a/modules/mono/register_types.h +++ b/modules/mono/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp index bd67b03c8e..3aaf726fc8 100644 --- a/modules/mono/signal_awaiter_utils.cpp +++ b/modules/mono/signal_awaiter_utils.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -48,18 +48,10 @@ Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signa } bool SignalAwaiterCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) { + // Only called if both instances are of type SignalAwaiterCallable. Static cast is safe. const SignalAwaiterCallable *a = static_cast<const SignalAwaiterCallable *>(p_a); const SignalAwaiterCallable *b = static_cast<const SignalAwaiterCallable *>(p_b); - - if (a->target_id != b->target_id) { - return false; - } - - if (a->signal != b->signal) { - return false; - } - - return true; + return a->awaiter_handle.handle == b->awaiter_handle.handle; } bool SignalAwaiterCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) { @@ -109,11 +101,15 @@ void SignalAwaiterCallable::call(const Variant **p_arguments, int p_argcount, Va "Resumed after await, but class instance is gone."); #endif - MonoArray *signal_args = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_argcount); + MonoArray *signal_args = nullptr; - for (int i = 0; i < p_argcount; i++) { - MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(*p_arguments[i]); - mono_array_setref(signal_args, i, boxed); + if (p_argcount > 0) { + signal_args = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_argcount); + + for (int i = 0; i < p_argcount; i++) { + MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(*p_arguments[i]); + mono_array_setref(signal_args, i, boxed); + } } MonoObject *awaiter = awaiter_handle.get_target(); diff --git a/modules/mono/signal_awaiter_utils.h b/modules/mono/signal_awaiter_utils.h index 18d1e43e14..4c77f8cfed 100644 --- a/modules/mono/signal_awaiter_utils.h +++ b/modules/mono/signal_awaiter_utils.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/utils/macros.h b/modules/mono/utils/macros.h index 60c9b9718a..4a220d89c8 100644 --- a/modules/mono/utils/macros.h +++ b/modules/mono/utils/macros.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/utils/mono_reg_utils.cpp b/modules/mono/utils/mono_reg_utils.cpp index 9902744743..27c2b2c5c1 100644 --- a/modules/mono/utils/mono_reg_utils.cpp +++ b/modules/mono/utils/mono_reg_utils.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -188,7 +188,7 @@ String find_msbuild_tools_path() { if (key == "installationPath") { String val = line.substr(sep_idx + 1, line.length()).strip_edges(); - ERR_BREAK(val.empty()); + ERR_BREAK(val.is_empty()); if (!val.ends_with("\\")) { val += "\\"; diff --git a/modules/mono/utils/mono_reg_utils.h b/modules/mono/utils/mono_reg_utils.h index cc3f1cb035..0e617761ea 100644 --- a/modules/mono/utils/mono_reg_utils.h +++ b/modules/mono/utils/mono_reg_utils.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/utils/osx_utils.cpp b/modules/mono/utils/osx_utils.cpp index 41be198bcf..f4216c8129 100644 --- a/modules/mono/utils/osx_utils.cpp +++ b/modules/mono/utils/osx_utils.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/utils/osx_utils.h b/modules/mono/utils/osx_utils.h index 92faead0fb..6704f19077 100644 --- a/modules/mono/utils/osx_utils.h +++ b/modules/mono/utils/osx_utils.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/utils/path_utils.cpp b/modules/mono/utils/path_utils.cpp index a24097924e..93d44628ac 100644 --- a/modules/mono/utils/path_utils.cpp +++ b/modules/mono/utils/path_utils.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -136,7 +136,7 @@ String realpath(const String &p_path) { } String join(const String &p_a, const String &p_b) { - if (p_a.empty()) { + if (p_a.is_empty()) { return p_b; } @@ -165,7 +165,7 @@ String relative_to_impl(const String &p_path, const String &p_relative_to) { } else { String base_dir = p_relative_to.get_base_dir(); - if (base_dir.length() <= 2 && (base_dir.empty() || base_dir.ends_with(":"))) { + if (base_dir.length() <= 2 && (base_dir.is_empty() || base_dir.ends_with(":"))) { return p_path; } diff --git a/modules/mono/utils/path_utils.h b/modules/mono/utils/path_utils.h index c19cb3bc8b..82b8f95f49 100644 --- a/modules/mono/utils/path_utils.h +++ b/modules/mono/utils/path_utils.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp index d70004657c..43de77005e 100644 --- a/modules/mono/utils/string_utils.cpp +++ b/modules/mono/utils/string_utils.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/utils/string_utils.h b/modules/mono/utils/string_utils.h index 99f3548f31..3290cb38b9 100644 --- a/modules/mono/utils/string_utils.h +++ b/modules/mono/utils/string_utils.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/ogg/SCsub b/modules/ogg/SCsub index e768fb4ae8..e415d92498 100644 --- a/modules/ogg/SCsub +++ b/modules/ogg/SCsub @@ -9,6 +9,9 @@ Import("env_modules") env_ogg = env_modules.Clone() # Thirdparty source files + +thirdparty_obj = [] + if env["builtin_libogg"]: thirdparty_dir = "#thirdparty/libogg/" thirdparty_sources = [ @@ -21,7 +24,16 @@ if env["builtin_libogg"]: env_thirdparty = env_ogg.Clone() env_thirdparty.disable_warnings() - env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) + env.modules_sources += thirdparty_obj + # Godot source files -env_ogg.add_source_files(env.modules_sources, "*.cpp") + +module_obj = [] + +env_ogg.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj + +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/ogg/register_types.cpp b/modules/ogg/register_types.cpp index 73c691397c..b23ea65378 100644 --- a/modules/ogg/register_types.cpp +++ b/modules/ogg/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/ogg/register_types.h b/modules/ogg/register_types.h index 849d27bb06..49d5ed9c80 100644 --- a/modules/ogg/register_types.h +++ b/modules/ogg/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/opensimplex/SCsub b/modules/opensimplex/SCsub index 52d8b145ef..86d77c3dfb 100644 --- a/modules/opensimplex/SCsub +++ b/modules/opensimplex/SCsub @@ -6,6 +6,9 @@ Import("env_modules") env_opensimplex = env_modules.Clone() # Thirdparty source files + +thirdparty_obj = [] + thirdparty_dir = "#thirdparty/misc/" thirdparty_sources = [ "open-simplex-noise.c", @@ -16,7 +19,15 @@ env_opensimplex.Prepend(CPPPATH=[thirdparty_dir]) env_thirdparty = env_opensimplex.Clone() env_thirdparty.disable_warnings() -env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) +env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) +env.modules_sources += thirdparty_obj + +# Godot source files + +module_obj = [] + +env_opensimplex.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj -# Godot's own source files -env_opensimplex.add_source_files(env.modules_sources, "*.cpp") +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/opensimplex/doc_classes/NoiseTexture.xml b/modules/opensimplex/doc_classes/NoiseTexture.xml index c06f3096de..7df261d2ba 100644 --- a/modules/opensimplex/doc_classes/NoiseTexture.xml +++ b/modules/opensimplex/doc_classes/NoiseTexture.xml @@ -5,7 +5,7 @@ </brief_description> <description> Uses an [OpenSimplexNoise] to fill the texture data. You can specify the texture size but keep in mind that larger textures will take longer to generate and seamless noise only works with square sized textures. - NoiseTexture can also generate normalmap textures. + NoiseTexture can also generate normal map textures. The class uses [Thread]s to generate the texture data internally, so [method Texture2D.get_data] may return [code]null[/code] if the generation process has not completed yet. In that case, you need to wait for the texture to be generated before accessing the data: [codeblock] var texture = preload("res://noise.tres") @@ -18,7 +18,7 @@ <methods> </methods> <members> - <member name="as_normalmap" type="bool" setter="set_as_normalmap" getter="is_normalmap" default="false"> + <member name="as_normal_map" type="bool" setter="set_as_normal_map" getter="is_normal_map" default="false"> If [code]true[/code], the resulting texture contains a normal map created from the original noise interpreted as a bump map. </member> <member name="bump_strength" type="float" setter="set_bump_strength" getter="get_bump_strength" default="8.0"> diff --git a/modules/opensimplex/doc_classes/OpenSimplexNoise.xml b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml index d89828037f..dcda5c2324 100644 --- a/modules/opensimplex/doc_classes/OpenSimplexNoise.xml +++ b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml @@ -24,7 +24,7 @@ <tutorials> </tutorials> <methods> - <method name="get_image"> + <method name="get_image" qualifiers="const"> <return type="Image"> </return> <argument index="0" name="width" type="int"> @@ -32,10 +32,10 @@ <argument index="1" name="height" type="int"> </argument> <description> - Generate a noise image with the requested [code]width[/code] and [code]height[/code], based on the current noise parameters. + Generate a noise image in [constant Image.FORMAT_L8] format with the requested [code]width[/code] and [code]height[/code], based on the current noise parameters. </description> </method> - <method name="get_noise_1d"> + <method name="get_noise_1d" qualifiers="const"> <return type="float"> </return> <argument index="0" name="x" type="float"> @@ -45,7 +45,7 @@ [b]Note:[/b] This method actually returns the 2D noise value [code][-1,1][/code] with fixed y-coordinate value 0.0. </description> </method> - <method name="get_noise_2d"> + <method name="get_noise_2d" qualifiers="const"> <return type="float"> </return> <argument index="0" name="x" type="float"> @@ -56,7 +56,7 @@ Returns the 2D noise value [code][-1,1][/code] at the given position. </description> </method> - <method name="get_noise_2dv"> + <method name="get_noise_2dv" qualifiers="const"> <return type="float"> </return> <argument index="0" name="pos" type="Vector2"> @@ -65,7 +65,7 @@ Returns the 2D noise value [code][-1,1][/code] at the given position. </description> </method> - <method name="get_noise_3d"> + <method name="get_noise_3d" qualifiers="const"> <return type="float"> </return> <argument index="0" name="x" type="float"> @@ -78,7 +78,7 @@ Returns the 3D noise value [code][-1,1][/code] at the given position. </description> </method> - <method name="get_noise_3dv"> + <method name="get_noise_3dv" qualifiers="const"> <return type="float"> </return> <argument index="0" name="pos" type="Vector3"> @@ -87,7 +87,7 @@ Returns the 3D noise value [code][-1,1][/code] at the given position. </description> </method> - <method name="get_noise_4d"> + <method name="get_noise_4d" qualifiers="const"> <return type="float"> </return> <argument index="0" name="x" type="float"> @@ -102,13 +102,13 @@ Returns the 4D noise value [code][-1,1][/code] at the given position. </description> </method> - <method name="get_seamless_image"> + <method name="get_seamless_image" qualifiers="const"> <return type="Image"> </return> <argument index="0" name="size" type="int"> </argument> <description> - Generate a tileable noise image, based on the current noise parameters. Generated seamless images are always square ([code]size[/code] × [code]size[/code]). + Generate a tileable noise image in [constant Image.FORMAT_L8] format, based on the current noise parameters. Generated seamless images are always square ([code]size[/code] × [code]size[/code]). </description> </method> </methods> diff --git a/modules/opensimplex/noise_texture.cpp b/modules/opensimplex/noise_texture.cpp index 1181e69cd3..1d75e46747 100644 --- a/modules/opensimplex/noise_texture.cpp +++ b/modules/opensimplex/noise_texture.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -40,7 +40,7 @@ NoiseTexture::NoiseTexture() { size = Vector2i(512, 512); seamless = false; - as_normalmap = false; + as_normal_map = false; bump_strength = 8.0; noise = Ref<OpenSimplexNoise>(); @@ -68,8 +68,8 @@ void NoiseTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("set_seamless", "seamless"), &NoiseTexture::set_seamless); ClassDB::bind_method(D_METHOD("get_seamless"), &NoiseTexture::get_seamless); - ClassDB::bind_method(D_METHOD("set_as_normalmap", "as_normalmap"), &NoiseTexture::set_as_normalmap); - ClassDB::bind_method(D_METHOD("is_normalmap"), &NoiseTexture::is_normalmap); + ClassDB::bind_method(D_METHOD("set_as_normal_map", "as_normal_map"), &NoiseTexture::set_as_normal_map); + ClassDB::bind_method(D_METHOD("is_normal_map"), &NoiseTexture::is_normal_map); ClassDB::bind_method(D_METHOD("set_bump_strength", "bump_strength"), &NoiseTexture::set_bump_strength); ClassDB::bind_method(D_METHOD("get_bump_strength"), &NoiseTexture::get_bump_strength); @@ -81,14 +81,14 @@ void NoiseTexture::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), "set_width", "get_width"); ADD_PROPERTY(PropertyInfo(Variant::INT, "height", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), "set_height", "get_height"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "seamless"), "set_seamless", "get_seamless"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "as_normalmap"), "set_as_normalmap", "is_normalmap"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "as_normal_map"), "set_as_normal_map", "is_normal_map"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bump_strength", PROPERTY_HINT_RANGE, "0,32,0.1,or_greater"), "set_bump_strength", "get_bump_strength"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "noise", PROPERTY_HINT_RESOURCE_TYPE, "OpenSimplexNoise"), "set_noise", "get_noise"); } void NoiseTexture::_validate_property(PropertyInfo &property) const { if (property.name == "bump_strength") { - if (!as_normalmap) { + if (!as_normal_map) { property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; } } @@ -148,8 +148,8 @@ Ref<Image> NoiseTexture::_generate_texture() { image = ref_noise->get_image(size.x, size.y); } - if (as_normalmap) { - image->bumpmap_to_normalmap(bump_strength); + if (as_normal_map) { + image->bump_map_to_normal_map(bump_strength); } return image; @@ -225,17 +225,17 @@ bool NoiseTexture::get_seamless() { return seamless; } -void NoiseTexture::set_as_normalmap(bool p_as_normalmap) { - if (p_as_normalmap == as_normalmap) { +void NoiseTexture::set_as_normal_map(bool p_as_normal_map) { + if (p_as_normal_map == as_normal_map) { return; } - as_normalmap = p_as_normalmap; + as_normal_map = p_as_normal_map; _queue_update(); _change_notify(); } -bool NoiseTexture::is_normalmap() { - return as_normalmap; +bool NoiseTexture::is_normal_map() { + return as_normal_map; } void NoiseTexture::set_bump_strength(float p_bump_strength) { @@ -243,7 +243,7 @@ void NoiseTexture::set_bump_strength(float p_bump_strength) { return; } bump_strength = p_bump_strength; - if (as_normalmap) { + if (as_normal_map) { _queue_update(); } } diff --git a/modules/opensimplex/noise_texture.h b/modules/opensimplex/noise_texture.h index 73960ba85f..9f6e2cbf43 100644 --- a/modules/opensimplex/noise_texture.h +++ b/modules/opensimplex/noise_texture.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -57,7 +57,7 @@ private: Ref<OpenSimplexNoise> noise; Vector2i size; bool seamless; - bool as_normalmap; + bool as_normal_map; float bump_strength; void _thread_done(const Ref<Image> &p_image); @@ -82,8 +82,8 @@ public: void set_seamless(bool p_seamless); bool get_seamless(); - void set_as_normalmap(bool p_as_normalmap); - bool is_normalmap(); + void set_as_normal_map(bool p_as_normal_map); + bool is_normal_map(); void set_bump_strength(float p_bump_strength); float get_bump_strength(); diff --git a/modules/opensimplex/open_simplex_noise.cpp b/modules/opensimplex/open_simplex_noise.cpp index b08219d258..e4e2e0613a 100644 --- a/modules/opensimplex/open_simplex_noise.cpp +++ b/modules/opensimplex/open_simplex_noise.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -63,7 +63,7 @@ void OpenSimplexNoise::set_seed(int p_seed) { emit_changed(); } -int OpenSimplexNoise::get_seed() { +int OpenSimplexNoise::get_seed() const { return seed; } @@ -102,9 +102,9 @@ void OpenSimplexNoise::set_lacunarity(float p_lacunarity) { emit_changed(); } -Ref<Image> OpenSimplexNoise::get_image(int p_width, int p_height) { +Ref<Image> OpenSimplexNoise::get_image(int p_width, int p_height) const { Vector<uint8_t> data; - data.resize(p_width * p_height * 4); + data.resize(p_width * p_height); uint8_t *wd8 = data.ptrw(); @@ -112,21 +112,17 @@ Ref<Image> OpenSimplexNoise::get_image(int p_width, int p_height) { for (int j = 0; j < p_width; j++) { float v = get_noise_2d(j, i); v = v * 0.5 + 0.5; // Normalize [0..1] - uint8_t value = uint8_t(CLAMP(v * 255.0, 0, 255)); - wd8[(i * p_width + j) * 4 + 0] = value; - wd8[(i * p_width + j) * 4 + 1] = value; - wd8[(i * p_width + j) * 4 + 2] = value; - wd8[(i * p_width + j) * 4 + 3] = 255; + wd8[(i * p_width + j)] = uint8_t(CLAMP(v * 255.0, 0, 255)); } } - Ref<Image> image = memnew(Image(p_width, p_height, false, Image::FORMAT_RGBA8, data)); + Ref<Image> image = memnew(Image(p_width, p_height, false, Image::FORMAT_L8, data)); return image; } -Ref<Image> OpenSimplexNoise::get_seamless_image(int p_size) { +Ref<Image> OpenSimplexNoise::get_seamless_image(int p_size) const { Vector<uint8_t> data; - data.resize(p_size * p_size * 4); + data.resize(p_size * p_size); uint8_t *wd8 = data.ptrw(); @@ -147,15 +143,11 @@ Ref<Image> OpenSimplexNoise::get_seamless_image(int p_size) { float v = get_noise_4d(x, y, z, w); v = v * 0.5 + 0.5; // Normalize [0..1] - uint8_t value = uint8_t(CLAMP(v * 255.0, 0, 255)); - wd8[(i * p_size + j) * 4 + 0] = value; - wd8[(i * p_size + j) * 4 + 1] = value; - wd8[(i * p_size + j) * 4 + 2] = value; - wd8[(i * p_size + j) * 4 + 3] = 255; + wd8[(i * p_size + j)] = uint8_t(CLAMP(v * 255.0, 0, 255)); } } - Ref<Image> image = memnew(Image(p_size, p_size, false, Image::FORMAT_RGBA8, data)); + Ref<Image> image = memnew(Image(p_size, p_size, false, Image::FORMAT_L8, data)); return image; } @@ -193,11 +185,11 @@ void OpenSimplexNoise::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lacunarity", PROPERTY_HINT_RANGE, "0.1,4.0,0.01"), "set_lacunarity", "get_lacunarity"); } -float OpenSimplexNoise::get_noise_1d(float x) { +float OpenSimplexNoise::get_noise_1d(float x) const { return get_noise_2d(x, 1.0); } -float OpenSimplexNoise::get_noise_2d(float x, float y) { +float OpenSimplexNoise::get_noise_2d(float x, float y) const { x /= period; y /= period; @@ -217,7 +209,7 @@ float OpenSimplexNoise::get_noise_2d(float x, float y) { return sum / max; } -float OpenSimplexNoise::get_noise_3d(float x, float y, float z) { +float OpenSimplexNoise::get_noise_3d(float x, float y, float z) const { x /= period; y /= period; z /= period; @@ -239,7 +231,7 @@ float OpenSimplexNoise::get_noise_3d(float x, float y, float z) { return sum / max; } -float OpenSimplexNoise::get_noise_4d(float x, float y, float z, float w) { +float OpenSimplexNoise::get_noise_4d(float x, float y, float z, float w) const { x /= period; y /= period; z /= period; diff --git a/modules/opensimplex/open_simplex_noise.h b/modules/opensimplex/open_simplex_noise.h index 835f8ed35e..f18dd4d798 100644 --- a/modules/opensimplex/open_simplex_noise.h +++ b/modules/opensimplex/open_simplex_noise.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,15 +37,15 @@ #include "thirdparty/misc/open-simplex-noise.h" -// The maximum number of octaves allowed. Note that these are statically allocated. -// Higher values become exponentially slower, so this shouldn't be set too high -// to avoid freezing the editor for long periods of time. -#define MAX_OCTAVES 9 - class OpenSimplexNoise : public Resource { GDCLASS(OpenSimplexNoise, Resource); OBJ_SAVE_TYPE(OpenSimplexNoise); + // The maximum number of octaves allowed. Note that these are statically allocated. + // Higher values become exponentially slower, so this shouldn't be set too high + // to avoid freezing the editor for long periods of time. + static const int MAX_OCTAVES = 9; + osn_context contexts[MAX_OCTAVES]; int seed; @@ -61,7 +61,7 @@ public: void _init_seeds(); void set_seed(int seed); - int get_seed(); + int get_seed() const; void set_octaves(int p_octaves); int get_octaves() const { return octaves; } @@ -75,22 +75,22 @@ public: void set_lacunarity(float p_lacunarity); float get_lacunarity() const { return lacunarity; } - Ref<Image> get_image(int p_width, int p_height); - Ref<Image> get_seamless_image(int p_size); + Ref<Image> get_image(int p_width, int p_height) const; + Ref<Image> get_seamless_image(int p_size) const; - float get_noise_1d(float x); - float get_noise_2d(float x, float y); - float get_noise_3d(float x, float y, float z); - float get_noise_4d(float x, float y, float z, float w); + float get_noise_1d(float x) const; + float get_noise_2d(float x, float y) const; + float get_noise_3d(float x, float y, float z) const; + float get_noise_4d(float x, float y, float z, float w) const; - _FORCE_INLINE_ float _get_octave_noise_2d(int octave, float x, float y) { return open_simplex_noise2(&(contexts[octave]), x, y); } - _FORCE_INLINE_ float _get_octave_noise_3d(int octave, float x, float y, float z) { return open_simplex_noise3(&(contexts[octave]), x, y, z); } - _FORCE_INLINE_ float _get_octave_noise_4d(int octave, float x, float y, float z, float w) { return open_simplex_noise4(&(contexts[octave]), x, y, z, w); } + _FORCE_INLINE_ float _get_octave_noise_2d(int octave, float x, float y) const { return open_simplex_noise2(&(contexts[octave]), x, y); } + _FORCE_INLINE_ float _get_octave_noise_3d(int octave, float x, float y, float z) const { return open_simplex_noise3(&(contexts[octave]), x, y, z); } + _FORCE_INLINE_ float _get_octave_noise_4d(int octave, float x, float y, float z, float w) const { return open_simplex_noise4(&(contexts[octave]), x, y, z, w); } // Convenience - _FORCE_INLINE_ float get_noise_2dv(Vector2 v) { return get_noise_2d(v.x, v.y); } - _FORCE_INLINE_ float get_noise_3dv(Vector3 v) { return get_noise_3d(v.x, v.y, v.z); } + _FORCE_INLINE_ float get_noise_2dv(const Vector2 &v) const { return get_noise_2d(v.x, v.y); } + _FORCE_INLINE_ float get_noise_3dv(const Vector3 &v) const { return get_noise_3d(v.x, v.y, v.z); } protected: static void _bind_methods(); diff --git a/modules/opensimplex/register_types.cpp b/modules/opensimplex/register_types.cpp index fef90cdce3..e9735a2cc8 100644 --- a/modules/opensimplex/register_types.cpp +++ b/modules/opensimplex/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/opensimplex/register_types.h b/modules/opensimplex/register_types.h index 51c6815eae..d72e37e3a3 100644 --- a/modules/opensimplex/register_types.h +++ b/modules/opensimplex/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/opus/SCsub b/modules/opus/SCsub index 52c61fa708..1437cd86df 100644 --- a/modules/opus/SCsub +++ b/modules/opus/SCsub @@ -10,6 +10,10 @@ Import("env_modules") env_opus = env_modules.Clone() # Thirdparty source files + +thirdparty_obj = [] + +# Thirdparty source files if env["builtin_opus"]: thirdparty_dir = "#thirdparty/opus/" @@ -233,7 +237,16 @@ if env["builtin_opus"]: env_thirdparty = env_opus.Clone() env_thirdparty.disable_warnings() - env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) + env.modules_sources += thirdparty_obj + + +# Godot source files + +module_obj = [] + +env_opus.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj -# Module files -env_opus.add_source_files(env.modules_sources, "register_types.cpp") +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/opus/register_types.cpp b/modules/opus/register_types.cpp index a4329e142c..02874a9a4b 100644 --- a/modules/opus/register_types.cpp +++ b/modules/opus/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/opus/register_types.h b/modules/opus/register_types.h index ad6e083c82..af889cf809 100644 --- a/modules/opus/register_types.h +++ b/modules/opus/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/pvr/SCsub b/modules/pvr/SCsub index e0baf851f1..36052cffed 100644 --- a/modules/pvr/SCsub +++ b/modules/pvr/SCsub @@ -6,6 +6,9 @@ Import("env_modules") env_pvr = env_modules.Clone() # Thirdparty source files + +thirdparty_obj = [] + # Not unbundled so far since not widespread as shared library thirdparty_dir = "#thirdparty/pvrtccompressor/" thirdparty_sources = [ @@ -21,7 +24,15 @@ env_pvr.Prepend(CPPPATH=[thirdparty_dir]) env_thirdparty = env_pvr.Clone() env_thirdparty.disable_warnings() -env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) +env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) +env.modules_sources += thirdparty_obj # Godot source files -env_pvr.add_source_files(env.modules_sources, "*.cpp") + +module_obj = [] + +env_pvr.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj + +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/pvr/image_compress_pvrtc.cpp b/modules/pvr/image_compress_pvrtc.cpp new file mode 100644 index 0000000000..d2d8976694 --- /dev/null +++ b/modules/pvr/image_compress_pvrtc.cpp @@ -0,0 +1,84 @@ +/*************************************************************************/ +/* image_compress_pvrtc.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "image_compress_pvrtc.h" + +#include "core/io/image.h" +#include "core/object/reference.h" + +#include <PvrTcEncoder.h> +#include <RgbaBitmap.h> + +static void _compress_pvrtc1_4bpp(Image *p_img) { + Ref<Image> img = p_img->duplicate(); + + bool make_mipmaps = false; + if (!img->is_size_po2() || img->get_width() != img->get_height()) { + make_mipmaps = img->has_mipmaps(); + img->resize_to_po2(true); + } + img->convert(Image::FORMAT_RGBA8); + if (!img->has_mipmaps() && make_mipmaps) { + img->generate_mipmaps(); + } + + bool use_alpha = img->detect_alpha(); + + Ref<Image> new_img; + new_img.instance(); + new_img->create(img->get_width(), img->get_height(), img->has_mipmaps(), use_alpha ? Image::FORMAT_PVRTC1_4A : Image::FORMAT_PVRTC1_4); + + Vector<uint8_t> data = new_img->get_data(); + { + uint8_t *wr = data.ptrw(); + const uint8_t *r = img->get_data().ptr(); + + for (int i = 0; i <= new_img->get_mipmap_count(); i++) { + int ofs, size, w, h; + img->get_mipmap_offset_size_and_dimensions(i, ofs, size, w, h); + Javelin::RgbaBitmap bm(w, h); + void *dst = (void *)bm.GetData(); + copymem(dst, &r[ofs], size); + Javelin::ColorRgba<unsigned char> *dp = bm.GetData(); + for (int j = 0; j < size / 4; j++) { + // Red and blue colors are swapped. + SWAP(dp[j].r, dp[j].b); + } + new_img->get_mipmap_offset_size_and_dimensions(i, ofs, size, w, h); + Javelin::PvrTcEncoder::EncodeRgba4Bpp(&wr[ofs], bm); + } + } + + p_img->create(new_img->get_width(), new_img->get_height(), new_img->has_mipmaps(), new_img->get_format(), data); +} + +void _register_pvrtc_compress_func() { + Image::_image_compress_pvrtc1_4bpp_func = _compress_pvrtc1_4bpp; +} diff --git a/modules/pvr/image_compress_pvrtc.h b/modules/pvr/image_compress_pvrtc.h new file mode 100644 index 0000000000..985076ce4d --- /dev/null +++ b/modules/pvr/image_compress_pvrtc.h @@ -0,0 +1,36 @@ +/*************************************************************************/ +/* image_compress_pvrtc.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef IMAGE_COMPRESS_PVRTC_H +#define IMAGE_COMPRESS_PVRTC_H + +void _register_pvrtc_compress_func(); + +#endif // IMAGE_COMPRESS_PVRTC_H diff --git a/modules/pvr/register_types.cpp b/modules/pvr/register_types.cpp index 1eb697bba3..aeac564c93 100644 --- a/modules/pvr/register_types.cpp +++ b/modules/pvr/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,6 +30,7 @@ #include "register_types.h" +#include "image_compress_pvrtc.h" #include "texture_loader_pvr.h" static Ref<ResourceFormatPVR> resource_loader_pvr; @@ -37,6 +38,8 @@ static Ref<ResourceFormatPVR> resource_loader_pvr; void register_pvr_types() { resource_loader_pvr.instance(); ResourceLoader::add_resource_format_loader(resource_loader_pvr); + + _register_pvrtc_compress_func(); } void unregister_pvr_types() { diff --git a/modules/pvr/register_types.h b/modules/pvr/register_types.h index 8318996a46..74fcfe2ce4 100644 --- a/modules/pvr/register_types.h +++ b/modules/pvr/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/pvr/texture_loader_pvr.cpp b/modules/pvr/texture_loader_pvr.cpp index 0923714387..70a3c8b5a9 100644 --- a/modules/pvr/texture_loader_pvr.cpp +++ b/modules/pvr/texture_loader_pvr.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -29,11 +29,8 @@ /*************************************************************************/ #include "texture_loader_pvr.h" -#include "PvrTcEncoder.h" -#include "RgbaBitmap.h" + #include "core/os/file_access.h" -#include <string.h> -#include <new> static void _pvrtc_decompress(Image *p_img); @@ -111,11 +108,11 @@ RES ResourceFormatPVR::load(const String &p_path, const String &p_original_path, switch (flags & 0xFF) { case 0x18: case 0xC: - format = (flags & PVR_HAS_ALPHA) ? Image::FORMAT_PVRTC2A : Image::FORMAT_PVRTC2; + format = (flags & PVR_HAS_ALPHA) ? Image::FORMAT_PVRTC1_2A : Image::FORMAT_PVRTC1_2; break; case 0x19: case 0xD: - format = (flags & PVR_HAS_ALPHA) ? Image::FORMAT_PVRTC4A : Image::FORMAT_PVRTC4; + format = (flags & PVR_HAS_ALPHA) ? Image::FORMAT_PVRTC1_4A : Image::FORMAT_PVRTC1_4; break; case 0x16: format = Image::FORMAT_L8; @@ -156,7 +153,7 @@ RES ResourceFormatPVR::load(const String &p_path, const String &p_original_path, } Ref<Image> image = memnew(Image(width, height, mipmaps, format, data)); - ERR_FAIL_COND_V(image->empty(), RES()); + ERR_FAIL_COND_V(image->is_empty(), RES()); Ref<ImageTexture> texture = memnew(ImageTexture); texture->create_from_image(image); @@ -183,53 +180,8 @@ String ResourceFormatPVR::get_resource_type(const String &p_path) const { return ""; } -static void _compress_pvrtc4(Image *p_img) { - Ref<Image> img = p_img->duplicate(); - - bool make_mipmaps = false; - if (!img->is_size_po2() || img->get_width() != img->get_height()) { - make_mipmaps = img->has_mipmaps(); - img->resize_to_po2(true); - } - img->convert(Image::FORMAT_RGBA8); - if (!img->has_mipmaps() && make_mipmaps) { - img->generate_mipmaps(); - } - - bool use_alpha = img->detect_alpha(); - - Ref<Image> new_img; - new_img.instance(); - new_img->create(img->get_width(), img->get_height(), img->has_mipmaps(), use_alpha ? Image::FORMAT_PVRTC4A : Image::FORMAT_PVRTC4); - - Vector<uint8_t> data = new_img->get_data(); - { - uint8_t *wr = data.ptrw(); - const uint8_t *r = img->get_data().ptr(); - - for (int i = 0; i <= new_img->get_mipmap_count(); i++) { - int ofs, size, w, h; - img->get_mipmap_offset_size_and_dimensions(i, ofs, size, w, h); - Javelin::RgbaBitmap bm(w, h); - void *dst = (void *)bm.GetData(); - copymem(dst, &r[ofs], size); - Javelin::ColorRgba<unsigned char> *dp = bm.GetData(); - for (int j = 0; j < size / 4; j++) { - /* red and blue colors are swapped. */ - SWAP(dp[j].r, dp[j].b); - } - new_img->get_mipmap_offset_size_and_dimensions(i, ofs, size, w, h); - Javelin::PvrTcEncoder::EncodeRgba4Bpp(&wr[ofs], bm); - } - } - - p_img->create(new_img->get_width(), new_img->get_height(), new_img->has_mipmaps(), new_img->get_format(), data); -} - ResourceFormatPVR::ResourceFormatPVR() { Image::_image_decompress_pvrtc = _pvrtc_decompress; - Image::_image_compress_pvrtc4_func = _compress_pvrtc4; - Image::_image_compress_pvrtc2_func = _compress_pvrtc4; } ///////////////////////////////////////////////////////// @@ -635,9 +587,9 @@ static void decompress_pvrtc(PVRTCBlock *p_comp_img, const int p_2bit, const int } static void _pvrtc_decompress(Image *p_img) { - ERR_FAIL_COND(p_img->get_format() != Image::FORMAT_PVRTC2 && p_img->get_format() != Image::FORMAT_PVRTC2A && p_img->get_format() != Image::FORMAT_PVRTC4 && p_img->get_format() != Image::FORMAT_PVRTC4A); + ERR_FAIL_COND(p_img->get_format() != Image::FORMAT_PVRTC1_2 && p_img->get_format() != Image::FORMAT_PVRTC1_2A && p_img->get_format() != Image::FORMAT_PVRTC1_4 && p_img->get_format() != Image::FORMAT_PVRTC1_4A); - bool _2bit = (p_img->get_format() == Image::FORMAT_PVRTC2 || p_img->get_format() == Image::FORMAT_PVRTC2A); + bool _2bit = (p_img->get_format() == Image::FORMAT_PVRTC1_2 || p_img->get_format() == Image::FORMAT_PVRTC1_2A); Vector<uint8_t> data = p_img->get_data(); const uint8_t *r = data.ptr(); diff --git a/modules/pvr/texture_loader_pvr.h b/modules/pvr/texture_loader_pvr.h index 07ef129689..da425c3237 100644 --- a/modules/pvr/texture_loader_pvr.h +++ b/modules/pvr/texture_loader_pvr.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/regex/SCsub b/modules/regex/SCsub index 2afacc1d9c..deb9db7591 100644 --- a/modules/regex/SCsub +++ b/modules/regex/SCsub @@ -5,6 +5,10 @@ Import("env_modules") env_regex = env_modules.Clone() +# Thirdparty source files + +thirdparty_obj = [] + if env["builtin_pcre2"]: thirdparty_dir = "#thirdparty/pcre2/src/" thirdparty_flags = ["PCRE2_STATIC", "HAVE_CONFIG_H", "SUPPORT_UNICODE"] @@ -52,11 +56,21 @@ if env["builtin_pcre2"]: env_pcre2 = env_regex.Clone() env_pcre2.disable_warnings() env_pcre2["OBJSUFFIX"] = "_" + width + env_pcre2["OBJSUFFIX"] - env_pcre2.add_source_files(env.modules_sources, thirdparty_sources) env_pcre2.Append(CPPDEFINES=[("PCRE2_CODE_UNIT_WIDTH", width)]) + env_pcre2.add_source_files(thirdparty_obj, thirdparty_sources) + env.modules_sources += thirdparty_obj pcre2_builtin("16") pcre2_builtin("32") + +# Godot source files + +module_obj = [] + env_regex.Append(CPPDEFINES=[("PCRE2_CODE_UNIT_WIDTH", 0)]) -env_regex.add_source_files(env.modules_sources, "*.cpp") +env_regex.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj + +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/regex/doc_classes/RegEx.xml b/modules/regex/doc_classes/RegEx.xml index 312275842a..b21f5d1e7a 100644 --- a/modules/regex/doc_classes/RegEx.xml +++ b/modules/regex/doc_classes/RegEx.xml @@ -39,8 +39,8 @@ var regex = RegEx.new() regex.compile("\\S+") # Negated whitespace character class. var results = [] - for match in regex.search_all("One Two \n\tThree"): - results.push_back(match.get_string()) + for result in regex.search_all("One Two \n\tThree"): + results.push_back(result.get_string()) # The `results` array now contains "One", "Two", "Three". [/codeblock] [b]Note:[/b] Godot's regex implementation is based on the [url=https://www.pcre.org/]PCRE2[/url] library. You can view the full pattern reference [url=https://www.pcre.org/current/doc/html/pcre2pattern.html]here[/url]. diff --git a/modules/regex/regex.cpp b/modules/regex/regex.cpp index c10a276eae..fe8136ef35 100644 --- a/modules/regex/regex.cpp +++ b/modules/regex/regex.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/regex/regex.h b/modules/regex/regex.h index 5b4798b65a..46505855d7 100644 --- a/modules/regex/regex.h +++ b/modules/regex/regex.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/regex/register_types.cpp b/modules/regex/register_types.cpp index d470fcdaeb..82f3eaf707 100644 --- a/modules/regex/register_types.cpp +++ b/modules/regex/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/regex/register_types.h b/modules/regex/register_types.h index cf377cdf5f..fe94cde954 100644 --- a/modules/regex/register_types.h +++ b/modules/regex/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/regex/tests/test_regex.h b/modules/regex/tests/test_regex.h index 4b9e7e18eb..c2d303b435 100644 --- a/modules/regex/tests/test_regex.h +++ b/modules/regex/tests/test_regex.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/register_module_types.h b/modules/register_module_types.h index acd9fc7c97..2cff8c54c4 100644 --- a/modules/register_module_types.h +++ b/modules/register_module_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/squish/SCsub b/modules/squish/SCsub index b31032403f..c9e29911d8 100644 --- a/modules/squish/SCsub +++ b/modules/squish/SCsub @@ -6,6 +6,9 @@ Import("env_modules") env_squish = env_modules.Clone() # Thirdparty source files + +thirdparty_obj = [] + if env["builtin_squish"]: thirdparty_dir = "#thirdparty/squish/" thirdparty_sources = [ @@ -26,7 +29,16 @@ if env["builtin_squish"]: env_thirdparty = env_squish.Clone() env_thirdparty.disable_warnings() - env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) + env.modules_sources += thirdparty_obj + # Godot source files -env_squish.add_source_files(env.modules_sources, "*.cpp") + +module_obj = [] + +env_squish.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj + +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/squish/image_compress_squish.cpp b/modules/squish/image_compress_squish.cpp index c510779317..cce08034df 100644 --- a/modules/squish/image_compress_squish.cpp +++ b/modules/squish/image_compress_squish.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/squish/image_compress_squish.h b/modules/squish/image_compress_squish.h index 11b9e1c833..301d30fcf1 100644 --- a/modules/squish/image_compress_squish.h +++ b/modules/squish/image_compress_squish.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/squish/register_types.cpp b/modules/squish/register_types.cpp index ad28aff058..451e9d8e93 100644 --- a/modules/squish/register_types.cpp +++ b/modules/squish/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/squish/register_types.h b/modules/squish/register_types.h index ab56c54d4a..0f87d64333 100644 --- a/modules/squish/register_types.h +++ b/modules/squish/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/stb_vorbis/SCsub b/modules/stb_vorbis/SCsub index 266c87c802..8fddb23dc8 100644 --- a/modules/stb_vorbis/SCsub +++ b/modules/stb_vorbis/SCsub @@ -6,11 +6,22 @@ Import("env_modules") env_stb_vorbis = env_modules.Clone() # Thirdparty source files + +thirdparty_obj = [] + thirdparty_sources = ["#thirdparty/misc/stb_vorbis.c"] env_thirdparty = env_stb_vorbis.Clone() env_thirdparty.disable_warnings() -env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) +env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) +env.modules_sources += thirdparty_obj + +# Godot source files + +module_obj = [] + +env_stb_vorbis.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj -# Godot's own source files -env_stb_vorbis.add_source_files(env.modules_sources, "*.cpp") +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp index 346833ab9c..4b2be47e74 100644 --- a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp +++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -124,7 +124,10 @@ AudioStreamPlaybackOGGVorbis::~AudioStreamPlaybackOGGVorbis() { Ref<AudioStreamPlayback> AudioStreamOGGVorbis::instance_playback() { Ref<AudioStreamPlaybackOGGVorbis> ovs; - ERR_FAIL_COND_V(data == nullptr, ovs); + ERR_FAIL_COND_V_MSG(data == nullptr, ovs, + "This AudioStreamOGGVorbis does not have an audio file assigned " + "to it. AudioStreamOGGVorbis should not be created from the " + "inspector or with `.new()`. Instead, load an audio file."); ovs.instance(); ovs->vorbis_stream = Ref<AudioStreamOGGVorbis>(this); diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.h b/modules/stb_vorbis/audio_stream_ogg_vorbis.h index 5070f2a078..efc8fc6c27 100644 --- a/modules/stb_vorbis/audio_stream_ogg_vorbis.h +++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/stb_vorbis/register_types.cpp b/modules/stb_vorbis/register_types.cpp index 13c26fc8cc..6f7eb53bc8 100644 --- a/modules/stb_vorbis/register_types.cpp +++ b/modules/stb_vorbis/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/stb_vorbis/register_types.h b/modules/stb_vorbis/register_types.h index f5a1dd31bc..d36d87606c 100644 --- a/modules/stb_vorbis/register_types.h +++ b/modules/stb_vorbis/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp b/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp index d68d050d34..ec1c30783a 100644 --- a/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp +++ b/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/stb_vorbis/resource_importer_ogg_vorbis.h b/modules/stb_vorbis/resource_importer_ogg_vorbis.h index 47f0039328..60fe3381fb 100644 --- a/modules/stb_vorbis/resource_importer_ogg_vorbis.h +++ b/modules/stb_vorbis/resource_importer_ogg_vorbis.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/svg/SCsub b/modules/svg/SCsub index 0bfba34fe5..c7228a8d0b 100644 --- a/modules/svg/SCsub +++ b/modules/svg/SCsub @@ -6,6 +6,9 @@ Import("env_modules") env_svg = env_modules.Clone() # Thirdparty source files + +thirdparty_obj = [] + thirdparty_dir = "#thirdparty/nanosvg/" thirdparty_sources = [ "nanosvg.cc", @@ -16,7 +19,15 @@ env_svg.Prepend(CPPPATH=[thirdparty_dir]) env_thirdparty = env_svg.Clone() env_thirdparty.disable_warnings() -env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) +env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) +env.modules_sources += thirdparty_obj + +# Godot source files + +module_obj = [] + +env_svg.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj -# Godot's own source files -env_svg.add_source_files(env.modules_sources, "*.cpp") +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/svg/image_loader_svg.cpp b/modules/svg/image_loader_svg.cpp index 8ca4452ac9..6ce3e4b4b3 100644 --- a/modules/svg/image_loader_svg.cpp +++ b/modules/svg/image_loader_svg.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/svg/image_loader_svg.h b/modules/svg/image_loader_svg.h index 8e478a40ce..e64175b172 100644 --- a/modules/svg/image_loader_svg.h +++ b/modules/svg/image_loader_svg.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/svg/register_types.cpp b/modules/svg/register_types.cpp index 9fbefd2cfe..1a611184d2 100644 --- a/modules/svg/register_types.cpp +++ b/modules/svg/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/svg/register_types.h b/modules/svg/register_types.h index a3d914e0cb..106ac9056f 100644 --- a/modules/svg/register_types.h +++ b/modules/svg/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub index 7403b01a4c..3589c8546d 100644 --- a/modules/text_server_adv/SCsub +++ b/modules/text_server_adv/SCsub @@ -35,10 +35,14 @@ def make_icu_data(target, source, env): g.write("#endif") +# Thirdparty source files + +thirdparty_obj = [] + if env["builtin_harfbuzz"]: env_harfbuzz = env_modules.Clone() + env_harfbuzz.disable_warnings() - # Thirdparty source files thirdparty_dir = "#thirdparty/harfbuzz/" thirdparty_sources = [ "src/hb-aat-layout.cc", @@ -107,6 +111,15 @@ if env["builtin_harfbuzz"]: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] + env_harfbuzz.Append( + CPPPATH=[ + "#thirdparty/harfbuzz/src", + "#thirdparty/freetype/include", + "#thirdparty/graphite/include", + "#thirdparty/icu4c/common/", + ] + ) + if env["platform"] == "android" or env["platform"] == "linuxbsd" or env["platform"] == "server": env_harfbuzz.Append(CCFLAGS=["-DHAVE_PTHREAD"]) @@ -117,14 +130,6 @@ if env["builtin_harfbuzz"]: env_harfbuzz.Append(CCFLAGS=["-DHB_NO_MT"]) env_harfbuzz.Append( - CPPPATH=[ - "#thirdparty/harfbuzz/src", - "#thirdparty/freetype/include", - "#thirdparty/graphite/include", - "#thirdparty/icu4c/common/", - ] - ) - env_harfbuzz.Append( CCFLAGS=[ "-DHAVE_ICU_BUILTIN", "-DHAVE_ICU", @@ -133,10 +138,9 @@ if env["builtin_harfbuzz"]: "-DGRAPHITE2_STATIC", ] ) - env_harfbuzz.disable_warnings() - env_thirdparty = env_harfbuzz.Clone() - env_thirdparty.disable_warnings() - lib = env_thirdparty.add_library("harfbuzz_builtin", thirdparty_sources) + + lib = env_harfbuzz.add_library("harfbuzz_builtin", thirdparty_sources) + thirdparty_obj += lib # Needs to be appended to arrive after libscene in the linker call, # but we don't want it to arrive *after* system libs, so manual hack @@ -151,10 +155,11 @@ if env["builtin_harfbuzz"]: if not inserted: env.Append(LIBS=[lib]) + if env["builtin_graphite"]: env_graphite = env_modules.Clone() + env_graphite.disable_warnings() - # Thirdparty source files thirdparty_dir = "#thirdparty/graphite/" thirdparty_sources = [ "src/gr_char_info.cpp", @@ -203,10 +208,9 @@ if env["builtin_graphite"]: "-DGRAPHITE2_NFILEFACE", ] ) - env_graphite.disable_warnings() - env_thirdparty = env_graphite.Clone() - env_thirdparty.disable_warnings() - lib = env_thirdparty.add_library("graphite_builtin", thirdparty_sources) + + lib = env_graphite.add_library("graphite_builtin", thirdparty_sources) + thirdparty_obj += lib # Needs to be appended to arrive after libscene in the linker call, # but we don't want it to arrive *after* system libs, so manual hack @@ -221,12 +225,12 @@ if env["builtin_graphite"]: if not inserted: env.Append(LIBS=[lib]) + if env["builtin_icu"]: env_icu = env_modules.Clone() + env_icu.disable_warnings() - # Thirdparty source files thirdparty_dir = "#thirdparty/icu4c/" - # Thirdparty source files thirdparty_sources = [ "common/appendable.cpp", "common/bmpset.cpp", @@ -457,10 +461,8 @@ if env["builtin_icu"]: ] ) - env_icu.disable_warnings() - env_thirdparty = env_icu.Clone() - env_thirdparty.disable_warnings() - lib = env_thirdparty.add_library("icu_builtin", thirdparty_sources) + lib = env_icu.add_library("icu_builtin", thirdparty_sources) + thirdparty_obj += lib # Needs to be appended to arrive after libscene in the linker call, # but we don't want it to arrive *after* system libs, so manual hack @@ -475,6 +477,11 @@ if env["builtin_icu"]: if not inserted: env.Append(LIBS=[lib]) + +# Godot source files + +module_obj = [] + if env_text_server_adv["tools"]: env_text_server_adv.Append(CXXFLAGS=["-DICU_STATIC_DATA"]) @@ -486,4 +493,9 @@ env_text_server_adv.Append( "#thirdparty/icu4c/common/", ] ) -env_text_server_adv.add_source_files(env.modules_sources, "*.cpp") + +env_text_server_adv.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj + +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/text_server_adv/bitmap_font_adv.cpp b/modules/text_server_adv/bitmap_font_adv.cpp index 10c3732fd7..01fa94aa7c 100644 --- a/modules/text_server_adv/bitmap_font_adv.cpp +++ b/modules/text_server_adv/bitmap_font_adv.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -175,8 +175,9 @@ static hb_bool_t hb_bmp_get_font_h_extents(hb_font_t *font, void *font_data, hb_ return true; } -static hb_font_funcs_t *_hb_bmp_get_font_funcs() { - hb_font_funcs_t *funcs = hb_font_funcs_create(); +static hb_font_funcs_t *funcs = nullptr; +void hb_bmp_create_font_funcs() { + funcs = hb_font_funcs_create(); hb_font_funcs_set_font_h_extents_func(funcs, hb_bmp_get_font_h_extents, nullptr, nullptr); //hb_font_funcs_set_font_v_extents_func (funcs, hb_bmp_get_font_v_extents, nullptr, nullptr); @@ -194,12 +195,17 @@ static hb_font_funcs_t *_hb_bmp_get_font_funcs() { //hb_font_funcs_set_glyph_from_name_func (funcs, hb_bmp_get_glyph_from_name, nullptr, nullptr); hb_font_funcs_make_immutable(funcs); +} - return funcs; +void hb_bmp_free_font_funcs() { + if (funcs != nullptr) { + hb_font_funcs_destroy(funcs); + funcs = nullptr; + } } static void _hb_bmp_font_set_funcs(hb_font_t *p_font, BitmapFontDataAdvanced *p_face, int p_size, bool p_unref) { - hb_font_set_funcs(p_font, _hb_bmp_get_font_funcs(), _hb_bmp_font_create(p_face, p_size, p_unref), _hb_bmp_font_destroy); + hb_font_set_funcs(p_font, funcs, _hb_bmp_font_create(p_face, p_size, p_unref), _hb_bmp_font_destroy); } hb_font_t *hb_bmp_font_create(BitmapFontDataAdvanced *p_face, int p_size, hb_destroy_func_t p_destroy) { @@ -384,7 +390,6 @@ Error BitmapFontDataAdvanced::load_from_memory(const uint8_t *p_data, size_t p_s chr.rect.position.y = c[2]; chr.rect.size.x = c[3]; chr.rect.size.y = c[4]; - chr.texture_idx = 0; if (c[7] < 0) { chr.advance.x = c[3]; } else { diff --git a/modules/text_server_adv/bitmap_font_adv.h b/modules/text_server_adv/bitmap_font_adv.h index b2fe7f43af..c314f1b087 100644 --- a/modules/text_server_adv/bitmap_font_adv.h +++ b/modules/text_server_adv/bitmap_font_adv.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,6 +33,9 @@ #include "font_adv.h" +void hb_bmp_create_font_funcs(); +void hb_bmp_free_font_funcs(); + struct BitmapFontDataAdvanced : public FontDataAdvanced { _THREAD_SAFE_CLASS_ diff --git a/modules/text_server_adv/dynamic_font_adv.cpp b/modules/text_server_adv/dynamic_font_adv.cpp index 90e5cc8831..fcefa60d98 100644 --- a/modules/text_server_adv/dynamic_font_adv.cpp +++ b/modules/text_server_adv/dynamic_font_adv.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,8 +32,7 @@ #include FT_STROKER_H #include FT_ADVANCES_H - -HashMap<String, Vector<uint8_t>> DynamicFontDataAdvanced::font_mem_cache; +#include FT_MULTIPLE_MASTERS_H DynamicFontDataAdvanced::DataAtSize *DynamicFontDataAdvanced::get_data_for_size(int p_size, int p_outline_size) { ERR_FAIL_COND_V(!valid, nullptr); @@ -55,11 +54,10 @@ DynamicFontDataAdvanced::DataAtSize *DynamicFontDataAdvanced::get_data_for_size( if (E != nullptr) { fds = E->get(); } else { - // FT_OPEN_STREAM is extremely slow only on Android. - if (OS::get_singleton()->get_name() == "Android" && font_mem == nullptr && font_path != String()) { - if (font_mem_cache.has(font_path)) { - font_mem = font_mem_cache[font_path].ptr(); - font_mem_size = font_mem_cache[font_path].size(); + if (font_mem == nullptr && font_path != String()) { + if (!font_mem_cache.is_empty()) { + font_mem = font_mem_cache.ptr(); + font_mem_size = font_mem_cache.size(); } else { FileAccess *f = FileAccess::open(font_path, FileAccess::READ); if (!f) { @@ -67,11 +65,9 @@ DynamicFontDataAdvanced::DataAtSize *DynamicFontDataAdvanced::get_data_for_size( } size_t len = f->get_len(); - font_mem_cache[font_path] = Vector<uint8_t>(); - Vector<uint8_t> &fontdata = font_mem_cache[font_path]; - fontdata.resize(len); - f->get_buffer(fontdata.ptrw(), len); - font_mem = fontdata.ptr(); + font_mem_cache.resize(len); + f->get_buffer(font_mem_cache.ptrw(), len); + font_mem = font_mem_cache.ptr(); font_mem_size = len; f->close(); } @@ -79,27 +75,7 @@ DynamicFontDataAdvanced::DataAtSize *DynamicFontDataAdvanced::get_data_for_size( int error = 0; fds = memnew(DataAtSize); - if (font_mem == nullptr && font_path != String()) { - FileAccess *f = FileAccess::open(font_path, FileAccess::READ); - if (!f) { - memdelete(fds); - ERR_FAIL_V_MSG(nullptr, "Cannot open font file '" + font_path + "'."); - } - - memset(&fds->stream, 0, sizeof(FT_StreamRec)); - fds->stream.base = nullptr; - fds->stream.size = f->get_len(); - fds->stream.pos = 0; - fds->stream.descriptor.pointer = f; - fds->stream.read = _ft_stream_io; - fds->stream.close = _ft_stream_close; - - FT_Open_Args fargs; - memset(&fargs, 0, sizeof(FT_Open_Args)); - fargs.flags = FT_OPEN_STREAM; - fargs.stream = &fds->stream; - error = FT_Open_Face(library, &fargs, 0, &fds->face); - } else if (font_mem) { + if (font_mem) { memset(&fds->stream, 0, sizeof(FT_StreamRec)); fds->stream.base = (unsigned char *)font_mem; fds->stream.size = font_mem_size; @@ -159,33 +135,89 @@ DynamicFontDataAdvanced::DataAtSize *DynamicFontDataAdvanced::get_data_for_size( memdelete(fds); ERR_FAIL_V_MSG(nullptr, "Error loading HB font."); } + if (p_outline_size != 0) { size_cache_outline[id] = fds; } else { size_cache[id] = fds; } - } + // Write variations. + if (fds->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) { + FT_MM_Var *amaster; + + FT_Get_MM_Var(fds->face, &amaster); + + Vector<hb_variation_t> hb_vars; + Vector<FT_Fixed> coords; + coords.resize(amaster->num_axis); + + FT_Get_Var_Design_Coordinates(fds->face, coords.size(), coords.ptrw()); + + for (FT_UInt i = 0; i < amaster->num_axis; i++) { + hb_variation_t var; + + // Reset to default. + var.tag = amaster->axis[i].tag; + var.value = (double)amaster->axis[i].def / 65536.f; + coords.write[i] = amaster->axis[i].def; + + if (variations.has(var.tag)) { + var.value = variations[var.tag]; + coords.write[i] = CLAMP(variations[var.tag] * 65536.f, amaster->axis[i].minimum, amaster->axis[i].maximum); + } + + hb_vars.push_back(var); + } + + FT_Set_Var_Design_Coordinates(fds->face, coords.size(), coords.ptrw()); + hb_font_set_variations(fds->hb_handle, hb_vars.is_empty() ? nullptr : &hb_vars[0], hb_vars.size()); + + FT_Done_MM_Var(library, amaster); + } + } return fds; } -unsigned long DynamicFontDataAdvanced::_ft_stream_io(FT_Stream stream, unsigned long offset, unsigned char *buffer, unsigned long count) { - FileAccess *f = (FileAccess *)stream->descriptor.pointer; - - if (f->get_position() != offset) { - f->seek(offset); +Dictionary DynamicFontDataAdvanced::get_variation_list() const { + _THREAD_SAFE_METHOD_ + DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(base_size); + if (fds == nullptr) { + return Dictionary(); } - if (count == 0) { - return 0; + + Dictionary ret; + // Read variations. + if (fds->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) { + FT_MM_Var *amaster; + + FT_Get_MM_Var(fds->face, &amaster); + + for (FT_UInt i = 0; i < amaster->num_axis; i++) { + ret[(int32_t)amaster->axis[i].tag] = Vector3i(amaster->axis[i].minimum / 65536, amaster->axis[i].maximum / 65536, amaster->axis[i].def / 65536); + } + + FT_Done_MM_Var(library, amaster); } + return ret; +} - return f->get_buffer(buffer, count); +void DynamicFontDataAdvanced::set_variation(const String &p_name, double p_value) { + _THREAD_SAFE_METHOD_ + int32_t tag = TS->name_to_tag(p_name); + if (!variations.has(tag) || (variations[tag] != p_value)) { + variations[tag] = p_value; + clear_cache(); + } } -void DynamicFontDataAdvanced::_ft_stream_close(FT_Stream stream) { - FileAccess *f = (FileAccess *)stream->descriptor.pointer; - f->close(); - memdelete(f); +double DynamicFontDataAdvanced::get_variation(const String &p_name) const { + _THREAD_SAFE_METHOD_ + int32_t tag = TS->name_to_tag(p_name); + if (!variations.has(tag)) { + return 0.f; + } + return variations[tag]; } Dictionary DynamicFontDataAdvanced::get_feature_list() const { @@ -222,8 +254,6 @@ Dictionary DynamicFontDataAdvanced::get_feature_list() const { DynamicFontDataAdvanced::TexturePosition DynamicFontDataAdvanced::find_texture_pos_for_glyph(DynamicFontDataAdvanced::DataAtSize *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height) { TexturePosition ret; ret.index = -1; - ret.x = 0; - ret.y = 0; int mw = p_width; int mh = p_height; @@ -402,8 +432,8 @@ DynamicFontDataAdvanced::Character DynamicFontDataAdvanced::bitmap_to_character( } Character chr; - chr.align = Vector2(xofs, -yofs) * p_data->scale_color_font / oversampling; - chr.advance = advance * p_data->scale_color_font / oversampling; + chr.align = (Vector2(xofs, -yofs) * p_data->scale_color_font / oversampling).round(); + chr.advance = (advance * p_data->scale_color_font / oversampling).round(); chr.texture_idx = tex_pos.index; chr.found = true; diff --git a/modules/text_server_adv/dynamic_font_adv.h b/modules/text_server_adv/dynamic_font_adv.h index 61cff3cde9..c35dd9390b 100644 --- a/modules/text_server_adv/dynamic_font_adv.h +++ b/modules/text_server_adv/dynamic_font_adv.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -116,7 +116,9 @@ private: const uint8_t *font_mem = nullptr; int font_mem_size = 0; String font_path; - static HashMap<String, Vector<uint8_t>> font_mem_cache; + Vector<uint8_t> font_mem_cache; + + Map<int32_t, double> variations; float rect_margin = 1.f; int base_size = 16; @@ -128,9 +130,6 @@ private: Map<CacheID, DataAtSize *> size_cache; Map<CacheID, DataAtSize *> size_cache_outline; - static unsigned long _ft_stream_io(FT_Stream stream, unsigned long offset, unsigned char *buffer, unsigned long count); - static void _ft_stream_close(FT_Stream stream); - DataAtSize *get_data_for_size(int p_size, int p_outline_size = 0); TexturePosition find_texture_pos_for_glyph(DataAtSize *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height); @@ -149,6 +148,10 @@ public: virtual float get_descent(int p_size) const override; virtual Dictionary get_feature_list() const override; + virtual Dictionary get_variation_list() const override; + + virtual void set_variation(const String &p_name, double p_value) override; + virtual double get_variation(const String &p_name) const override; virtual float get_underline_position(int p_size) const override; virtual float get_underline_thickness(int p_size) const override; diff --git a/modules/text_server_adv/font_adv.h b/modules/text_server_adv/font_adv.h index 232d6d7d08..4bbd2dd4bf 100644 --- a/modules/text_server_adv/font_adv.h +++ b/modules/text_server_adv/font_adv.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -50,6 +50,10 @@ struct FontDataAdvanced { virtual float get_descent(int p_size) const = 0; virtual Dictionary get_feature_list() const { return Dictionary(); }; + virtual Dictionary get_variation_list() const { return Dictionary(); }; + + virtual void set_variation(const String &p_name, double p_value){}; + virtual double get_variation(const String &p_name) const { return 0; }; virtual float get_underline_position(int p_size) const = 0; virtual float get_underline_thickness(int p_size) const = 0; diff --git a/modules/text_server_adv/icu_data/icudata_stub.cpp b/modules/text_server_adv/icu_data/icudata_stub.cpp index 13f0ac0c50..187001f33a 100644 --- a/modules/text_server_adv/icu_data/icudata_stub.cpp +++ b/modules/text_server_adv/icu_data/icudata_stub.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/text_server_adv/register_types.cpp b/modules/text_server_adv/register_types.cpp index 68117e0380..abefa83b9b 100644 --- a/modules/text_server_adv/register_types.cpp +++ b/modules/text_server_adv/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/text_server_adv/register_types.h b/modules/text_server_adv/register_types.h index 8319ddfd5d..ddd1190f40 100644 --- a/modules/text_server_adv/register_types.h +++ b/modules/text_server_adv/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/text_server_adv/script_iterator.cpp b/modules/text_server_adv/script_iterator.cpp index 8a2b2cced0..8f23bb9e02 100644 --- a/modules/text_server_adv/script_iterator.cpp +++ b/modules/text_server_adv/script_iterator.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -56,11 +56,12 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length int paren_sp = -1; int start_sp = paren_sp; UErrorCode err = U_ZERO_ERROR; + const char32_t *str = p_string.ptr(); do { script_code = USCRIPT_COMMON; for (script_start = script_end; script_end < p_length; script_end++) { - UChar32 ch = p_string[script_end]; + UChar32 ch = str[script_end]; UScriptCode sc = uscript_getScript(ch, &err); if (U_FAILURE(err)) { ERR_FAIL_MSG(u_errorName(err)); diff --git a/modules/text_server_adv/script_iterator.h b/modules/text_server_adv/script_iterator.h index 4523aa2767..ad476f7c75 100644 --- a/modules/text_server_adv/script_iterator.h +++ b/modules/text_server_adv/script_iterator.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index e391777eea..8b05611089 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -132,7 +132,7 @@ _FORCE_INLINE_ bool is_linebreak(char32_t p_char) { /*************************************************************************/ String TextServerAdvanced::interface_name = "ICU / HarfBuzz / Graphite"; -uint32_t TextServerAdvanced::interface_features = FEATURE_BIDI_LAYOUT | FEATURE_VERTICAL_LAYOUT | FEATURE_SHAPING | FEATURE_KASHIDA_JUSTIFICATION | FEATURE_BREAK_ITERATORS | FEATURE_USE_SUPPORT_DATA; +uint32_t TextServerAdvanced::interface_features = FEATURE_BIDI_LAYOUT | FEATURE_VERTICAL_LAYOUT | FEATURE_SHAPING | FEATURE_KASHIDA_JUSTIFICATION | FEATURE_BREAK_ITERATORS | FEATURE_USE_SUPPORT_DATA | FEATURE_FONT_VARIABLE; bool TextServerAdvanced::has_feature(Feature p_feature) { return (interface_features & p_feature) == p_feature; @@ -171,7 +171,7 @@ bool TextServerAdvanced::load_support_data(const String &p_filename) { } #else if (icu_data == nullptr) { - String filename = (p_filename.empty()) ? String("res://") + _MKSTR(ICU_DATA_NAME) : p_filename; + String filename = (p_filename.is_empty()) ? String("res://") + _MKSTR(ICU_DATA_NAME) : p_filename; FileAccess *f = FileAccess::open(filename, FileAccess::READ); if (!f) { @@ -622,6 +622,27 @@ bool TextServerAdvanced::font_get_antialiased(RID p_font) const { return fd->get_antialiased(); } +Dictionary TextServerAdvanced::font_get_variation_list(RID p_font) const { + _THREAD_SAFE_METHOD_ + const FontDataAdvanced *fd = font_owner.getornull(p_font); + ERR_FAIL_COND_V(!fd, Dictionary()); + return fd->get_variation_list(); +} + +void TextServerAdvanced::font_set_variation(RID p_font, const String &p_name, double p_value) { + _THREAD_SAFE_METHOD_ + FontDataAdvanced *fd = font_owner.getornull(p_font); + ERR_FAIL_COND(!fd); + fd->set_variation(p_name, p_value); +} + +double TextServerAdvanced::font_get_variation(RID p_font, const String &p_name) const { + _THREAD_SAFE_METHOD_ + const FontDataAdvanced *fd = font_owner.getornull(p_font); + ERR_FAIL_COND_V(!fd, 0); + return fd->get_variation(p_name); +} + void TextServerAdvanced::font_set_distance_field_hint(RID p_font, bool p_distance_field) { _THREAD_SAFE_METHOD_ FontDataAdvanced *fd = font_owner.getornull(p_font); @@ -1040,7 +1061,7 @@ bool TextServerAdvanced::shaped_text_add_string(RID p_shaped, const String &p_te ERR_FAIL_COND_V(!sd, false); ERR_FAIL_COND_V(p_size <= 0, false); - if (p_text.empty()) { + if (p_text.is_empty()) { return true; } @@ -1108,7 +1129,9 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key, sd->width = 0; sd->upos = 0; sd->uthk = 0; - for (int i = 0; i < sd->glyphs.size(); i++) { + int sd_size = sd->glyphs.size(); + + for (int i = 0; i < sd_size; i++) { Glyph gl = sd->glyphs[i]; Variant key; if (gl.count == 1) { @@ -1128,8 +1151,8 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key, sd->ascent = MAX(sd->ascent, sd->objects[key].rect.size.y); } break; case VALIGN_CENTER: { - sd->ascent = MAX(sd->ascent, sd->objects[key].rect.size.y / 2); - sd->descent = MAX(sd->descent, sd->objects[key].rect.size.y / 2); + sd->ascent = MAX(sd->ascent, Math::round(sd->objects[key].rect.size.y / 2)); + sd->descent = MAX(sd->descent, Math::round(sd->objects[key].rect.size.y / 2)); } break; case VALIGN_BOTTOM: { sd->descent = MAX(sd->descent, sd->objects[key].rect.size.y); @@ -1144,8 +1167,8 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key, sd->ascent = MAX(sd->ascent, sd->objects[key].rect.size.x); } break; case VALIGN_CENTER: { - sd->ascent = MAX(sd->ascent, sd->objects[key].rect.size.x / 2); - sd->descent = MAX(sd->descent, sd->objects[key].rect.size.x / 2); + sd->ascent = MAX(sd->ascent, Math::round(sd->objects[key].rect.size.x / 2)); + sd->descent = MAX(sd->descent, Math::round(sd->objects[key].rect.size.x / 2)); } break; case VALIGN_BOTTOM: { sd->descent = MAX(sd->descent, sd->objects[key].rect.size.x); @@ -1160,19 +1183,19 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key, sd->ascent = MAX(sd->ascent, MAX(fd->get_ascent(gl.font_size), -gl.y_off)); sd->descent = MAX(sd->descent, MAX(fd->get_descent(gl.font_size), gl.y_off)); } else { - sd->ascent = MAX(sd->ascent, fd->get_advance(gl.index, gl.font_size).x * 0.5); - sd->descent = MAX(sd->descent, fd->get_advance(gl.index, gl.font_size).x * 0.5); + sd->ascent = MAX(sd->ascent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5)); + sd->descent = MAX(sd->descent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5)); } sd->upos = MAX(sd->upos, font_get_underline_position(gl.font_rid, gl.font_size)); sd->uthk = MAX(sd->uthk, font_get_underline_thickness(gl.font_rid, gl.font_size)); } else if (sd->preserve_invalid || (sd->preserve_control && is_control(gl.index))) { // Glyph not found, replace with hex code box. if (sd->orientation == ORIENTATION_HORIZONTAL) { - sd->ascent = MAX(sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f); - sd->descent = MAX(sd->descent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f); + sd->ascent = MAX(sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f)); + sd->descent = MAX(sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f)); } else { - sd->ascent = MAX(sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f); - sd->descent = MAX(sd->descent, get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f); + sd->ascent = MAX(sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f)); + sd->descent = MAX(sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f)); } } sd->width += gl.advance * gl.repeat; @@ -1248,6 +1271,11 @@ RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_leng new_sd->utf16 = new_sd->text.utf16(); new_sd->script_iter = memnew(ScriptIterator(new_sd->text, 0, new_sd->text.length())); + int sd_size = sd->glyphs.size(); + const Glyph *sd_glyphs = sd->glyphs.ptr(); + const FontDataAdvanced *fd = nullptr; + RID prev_rid = RID(); + for (int ov = 0; ov < sd->bidi_override.size(); ov++) { UErrorCode err = U_ZERO_ERROR; @@ -1280,21 +1308,23 @@ RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_leng int32_t bidi_run_start = _convert_pos(sd, sd->bidi_override[ov].x + start + _bidi_run_start); int32_t bidi_run_end = _convert_pos(sd, sd->bidi_override[ov].x + start + _bidi_run_start + _bidi_run_length); - for (int j = 0; j < sd->glyphs.size(); j++) { - if ((sd->glyphs[j].start >= bidi_run_start) && (sd->glyphs[j].end <= bidi_run_end)) { + for (int j = 0; j < sd_size; j++) { + if ((sd_glyphs[j].start >= bidi_run_start) && (sd_glyphs[j].end <= bidi_run_end)) { // Copy glyphs. - Glyph gl = sd->glyphs[j]; + Glyph gl = sd_glyphs[j]; Variant key; + bool find_embedded = false; if (gl.count == 1) { for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) { if (E->get().pos == gl.start) { + find_embedded = true; key = E->key(); new_sd->objects[key] = E->get(); break; } } } - if (key != Variant()) { + if (find_embedded) { if (new_sd->orientation == ORIENTATION_HORIZONTAL) { new_sd->objects[key].rect.position.x = new_sd->width; new_sd->width += new_sd->objects[key].rect.size.x; @@ -1303,8 +1333,8 @@ RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_leng new_sd->ascent = MAX(new_sd->ascent, new_sd->objects[key].rect.size.y); } break; case VALIGN_CENTER: { - new_sd->ascent = MAX(new_sd->ascent, new_sd->objects[key].rect.size.y / 2); - new_sd->descent = MAX(new_sd->descent, new_sd->objects[key].rect.size.y / 2); + new_sd->ascent = MAX(new_sd->ascent, Math::round(new_sd->objects[key].rect.size.y / 2)); + new_sd->descent = MAX(new_sd->descent, Math::round(new_sd->objects[key].rect.size.y / 2)); } break; case VALIGN_BOTTOM: { new_sd->descent = MAX(new_sd->descent, new_sd->objects[key].rect.size.y); @@ -1318,8 +1348,8 @@ RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_leng new_sd->ascent = MAX(new_sd->ascent, new_sd->objects[key].rect.size.x); } break; case VALIGN_CENTER: { - new_sd->ascent = MAX(new_sd->ascent, new_sd->objects[key].rect.size.x / 2); - new_sd->descent = MAX(new_sd->descent, new_sd->objects[key].rect.size.x / 2); + new_sd->ascent = MAX(new_sd->ascent, Math::round(new_sd->objects[key].rect.size.x / 2)); + new_sd->descent = MAX(new_sd->descent, Math::round(new_sd->objects[key].rect.size.x / 2)); } break; case VALIGN_BOTTOM: { new_sd->descent = MAX(new_sd->descent, new_sd->objects[key].rect.size.x); @@ -1327,23 +1357,26 @@ RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_leng } } } else { - const FontDataAdvanced *fd = font_owner.getornull(gl.font_rid); + if (prev_rid != gl.font_rid) { + fd = font_owner.getornull(gl.font_rid); + prev_rid = gl.font_rid; + } if (fd != nullptr) { if (new_sd->orientation == ORIENTATION_HORIZONTAL) { new_sd->ascent = MAX(new_sd->ascent, MAX(fd->get_ascent(gl.font_size), -gl.y_off)); new_sd->descent = MAX(new_sd->descent, MAX(fd->get_descent(gl.font_size), gl.y_off)); } else { - new_sd->ascent = MAX(new_sd->ascent, fd->get_advance(gl.index, gl.font_size).x * 0.5); - new_sd->descent = MAX(new_sd->descent, fd->get_advance(gl.index, gl.font_size).x * 0.5); + new_sd->ascent = MAX(new_sd->ascent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5)); + new_sd->descent = MAX(new_sd->descent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5)); } } else if (new_sd->preserve_invalid || (new_sd->preserve_control && is_control(gl.index))) { // Glyph not found, replace with hex code box. if (new_sd->orientation == ORIENTATION_HORIZONTAL) { - new_sd->ascent = MAX(new_sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f); - new_sd->descent = MAX(new_sd->descent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f); + new_sd->ascent = MAX(new_sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f)); + new_sd->descent = MAX(new_sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f)); } else { - new_sd->ascent = MAX(new_sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f); - new_sd->descent = MAX(new_sd->descent, get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f); + new_sd->ascent = MAX(new_sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f)); + new_sd->descent = MAX(new_sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f)); } } new_sd->width += gl.advance * gl.repeat; @@ -1491,9 +1524,9 @@ float TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, float p_width, if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE) { float old_adv = gl.advance; if ((gl.flags & GRAPHEME_IS_VIRTUAL) == GRAPHEME_IS_VIRTUAL) { - gl.advance = MAX(gl.advance + delta_width_per_space, 0.f); + gl.advance = Math::round(MAX(gl.advance + delta_width_per_space, 0.f)); } else { - gl.advance = MAX(gl.advance + delta_width_per_space, 0.05 * gl.font_size); + gl.advance = Math::round(MAX(gl.advance + delta_width_per_space, 0.05 * gl.font_size)); } sd->width += (gl.advance - old_adv); } @@ -1529,8 +1562,10 @@ float TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vector<float delta = -1; } + Glyph *gl = sd->glyphs.ptrw(); + for (int i = start; i != end; i += delta) { - if ((sd->glyphs[i].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB) { + if ((gl[i].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB) { float tab_off = 0.f; while (tab_off <= off) { tab_off += p_tab_stops[tab_index]; @@ -1539,13 +1574,13 @@ float TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vector<float tab_index = 0; } } - float old_adv = sd->glyphs.write[i].advance; - sd->glyphs.write[i].advance = (tab_off - off); - sd->width += sd->glyphs.write[i].advance - old_adv; + float old_adv = gl[i].advance; + gl[i].advance = tab_off - off; + sd->width += gl[i].advance - old_adv; off = 0; continue; } - off += sd->glyphs[i].advance * sd->glyphs[i].repeat; + off += gl[i].advance * gl[i].repeat; } return 0.f; @@ -1565,8 +1600,7 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) { const UChar *data = sd->utf16.ptr(); - Map<int, bool> breaks; - + HashMap<int, bool> breaks; UErrorCode err = U_ZERO_ERROR; for (int i = 0; i < sd->spans.size(); i++) { UBreakIterator *bi = ubrk_open(UBRK_LINE, sd->spans[i].language.ascii().get_data(), data + _convert_pos_inv(sd, sd->spans[i].start), _convert_pos_inv(sd, sd->spans[i].end - sd->spans[i].start), &err); @@ -1598,40 +1632,52 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) { sd->sort_valid = false; sd->glyphs_logical.clear(); + int sd_size = sd->glyphs.size(); + const char32_t *ch = sd->text.ptr(); + Glyph *sd_glyphs = sd->glyphs.ptrw(); - for (int i = 0; i < sd->glyphs.size(); i++) { - if (sd->glyphs[i].count > 0) { - char32_t c = sd->text[sd->glyphs[i].start - sd->start]; + for (int i = 0; i < sd_size; i++) { + if (sd_glyphs[i].count > 0) { + char32_t c = ch[sd_glyphs[i].start - sd->start]; if (c == 0xfffc) { continue; } if (c == 0x0009 || c == 0x000b) { - sd->glyphs.write[i].flags |= GRAPHEME_IS_TAB; + sd_glyphs[i].flags |= GRAPHEME_IS_TAB; + } + if (is_whitespace(c)) { + sd_glyphs[i].flags |= GRAPHEME_IS_SPACE; + } + if (u_ispunct(c)) { + sd_glyphs[i].flags |= GRAPHEME_IS_PUNCTUATION; } if (breaks.has(sd->glyphs[i].start)) { if (breaks[sd->glyphs[i].start]) { - sd->glyphs.write[i].flags |= GRAPHEME_IS_BREAK_HARD; + sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_HARD; } else { if (is_whitespace(c)) { - sd->glyphs.write[i].flags |= GRAPHEME_IS_BREAK_SOFT; - sd->glyphs.write[i].flags |= GRAPHEME_IS_SPACE; + sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_SOFT; } else { TextServer::Glyph gl; - gl.start = sd->glyphs[i].start; - gl.end = sd->glyphs[i].end; + gl.start = sd_glyphs[i].start; + gl.end = sd_glyphs[i].end; gl.count = 1; - gl.repeat = 1; - gl.font_rid = sd->glyphs[i].font_rid; - gl.font_size = sd->glyphs[i].font_size; + gl.font_rid = sd_glyphs[i].font_rid; + gl.font_size = sd_glyphs[i].font_size; gl.flags = GRAPHEME_IS_BREAK_SOFT | GRAPHEME_IS_VIRTUAL; - sd->glyphs.insert(i + sd->glyphs[i].count, gl); // insert after - i += sd->glyphs[i].count; + sd->glyphs.insert(i + sd_glyphs[i].count, gl); // insert after + + // Update write pointer and size. + sd_size = sd->glyphs.size(); + sd_glyphs = sd->glyphs.ptrw(); + + i += sd_glyphs[i].count; continue; } } } - i += (sd->glyphs[i].count - 1); + i += (sd_glyphs[i].count - 1); } } @@ -1725,6 +1771,10 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) { shaped_text_update_breaks(p_shaped); } + if (sd->justification_ops_valid) { + return true; // Noting to do. + } + const UChar *data = sd->utf16.ptr(); int32_t data_size = sd->utf16.length(); @@ -1755,9 +1805,9 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) { if (ubrk_getRuleStatus(bi) != UBRK_WORD_NONE) { int i = _convert_pos(sd, ubrk_current(bi)); jstops[i + sd->start] = false; - int ks = _generate_kashida_justification_opportunies(sd->text, limit, i) + sd->start; + int ks = _generate_kashida_justification_opportunies(sd->text, limit, i); if (ks != -1) { - jstops[ks] = true; + jstops[ks + sd->start] = true; } limit = i; } @@ -1767,9 +1817,10 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) { sd->sort_valid = false; sd->glyphs_logical.clear(); + int sd_size = sd->glyphs.size(); if (jstops.size() > 0) { - for (int i = 0; i < sd->glyphs.size(); i++) { + for (int i = 0; i < sd_size; i++) { if (sd->glyphs[i].count > 0) { if (jstops.has(sd->glyphs[i].start)) { char32_t c = sd->text[sd->glyphs[i].start - sd->start]; @@ -1803,7 +1854,6 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) { gl.start = sd->glyphs[i].start; gl.end = sd->glyphs[i].end; gl.count = 1; - gl.repeat = 1; gl.font_rid = sd->glyphs[i].font_rid; gl.font_size = sd->glyphs[i].font_size; gl.flags = GRAPHEME_IS_SPACE | GRAPHEME_IS_VIRTUAL; @@ -1838,8 +1888,6 @@ TextServer::Glyph TextServerAdvanced::_shape_single_glyph(ShapedTextDataAdvanced // Process glyphs. TextServer::Glyph gl; - gl.start = -1; - gl.end = -1; if (p_direction == HB_DIRECTION_RTL || p_direction == HB_DIRECTION_BTT) { gl.flags |= TextServer::GRAPHEME_IS_RTL; @@ -1850,15 +1898,15 @@ TextServer::Glyph TextServerAdvanced::_shape_single_glyph(ShapedTextDataAdvanced if (glyph_count > 0) { if (p_sd->orientation == ORIENTATION_HORIZONTAL) { - gl.advance = glyph_pos[0].x_advance / (64.0 / fd->get_font_scale(p_font_size)); + gl.advance = Math::round(glyph_pos[0].x_advance / (64.0 / fd->get_font_scale(p_font_size))); } else { - gl.advance = -glyph_pos[0].y_advance / (64.0 / fd->get_font_scale(p_font_size)); + gl.advance = -Math::round(glyph_pos[0].y_advance / (64.0 / fd->get_font_scale(p_font_size))); } gl.count = 1; gl.index = glyph_info[0].codepoint; - gl.x_off = glyph_pos[0].x_offset / (64.0 / fd->get_font_scale(p_font_size)); - gl.y_off = -glyph_pos[0].y_offset / (64.0 / fd->get_font_scale(p_font_size)); + gl.x_off = Math::round(glyph_pos[0].x_offset / (64.0 / fd->get_font_scale(p_font_size))); + gl.y_off = -Math::round(glyph_pos[0].y_offset / (64.0 / fd->get_font_scale(p_font_size))); if ((glyph_info[0].codepoint != 0) || !u_isgraph(p_char)) { gl.flags |= GRAPHEME_IS_VALID; @@ -1873,8 +1921,9 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star fd = font_owner.getornull(p_fonts[p_fb_index]); } + int fs = p_sd->spans[p_span].font_size; if (fd == nullptr) { - // Add fallback glyohs + // Add fallback glyphs for (int i = p_start; i < p_end; i++) { if (p_sd->preserve_invalid || (p_sd->preserve_control && is_control(p_sd->text[i]))) { TextServer::Glyph gl; @@ -1882,20 +1931,19 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star gl.end = i + 1; gl.count = 1; gl.index = p_sd->text[i]; - gl.font_size = p_sd->spans[p_span].font_size; + gl.font_size = fs; gl.font_rid = RID(); - gl.flags = 0; if (p_direction == HB_DIRECTION_RTL || p_direction == HB_DIRECTION_BTT) { gl.flags |= TextServer::GRAPHEME_IS_RTL; } if (p_sd->orientation == ORIENTATION_HORIZONTAL) { - gl.advance = get_hex_code_box_size(gl.font_size, gl.index).x; - p_sd->ascent = MAX(p_sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f); - p_sd->descent = MAX(p_sd->descent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f); + gl.advance = get_hex_code_box_size(fs, gl.index).x; + p_sd->ascent = MAX(p_sd->ascent, Math::round(get_hex_code_box_size(fs, gl.index).y * 0.75f)); + p_sd->descent = MAX(p_sd->descent, Math::round(get_hex_code_box_size(fs, gl.index).y * 0.25f)); } else { - gl.advance = get_hex_code_box_size(gl.font_size, gl.index).y; - p_sd->ascent = MAX(p_sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f); - p_sd->descent = MAX(p_sd->descent, get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f); + gl.advance = get_hex_code_box_size(fs, gl.index).y; + p_sd->ascent = MAX(p_sd->ascent, Math::round(get_hex_code_box_size(fs, gl.index).x * 0.5f)); + p_sd->descent = MAX(p_sd->descent, Math::round(get_hex_code_box_size(fs, gl.index).x * 0.5f)); } p_sd->width += gl.advance; @@ -1905,7 +1953,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star return; } - hb_font_t *hb_font = fd->get_hb_handle(p_sd->spans[p_span].font_size); + hb_font_t *hb_font = fd->get_hb_handle(fs); ERR_FAIL_COND(hb_font == nullptr); hb_buffer_clear_contents(p_sd->hb_buffer); @@ -1936,7 +1984,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star ftrs.push_back(feature); } } - hb_shape(hb_font, p_sd->hb_buffer, ftrs.empty() ? nullptr : &ftrs[0], ftrs.size()); + hb_shape(hb_font, p_sd->hb_buffer, ftrs.is_empty() ? nullptr : &ftrs[0], ftrs.size()); unsigned int glyph_count = 0; hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(p_sd->hb_buffer, &glyph_count); @@ -1981,17 +2029,17 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star gl.count = 0; gl.font_rid = p_fonts[p_fb_index]; - gl.font_size = p_sd->spans[p_span].font_size; + gl.font_size = fs; gl.index = glyph_info[i].codepoint; if (gl.index != 0) { if (p_sd->orientation == ORIENTATION_HORIZONTAL) { - gl.advance = glyph_pos[i].x_advance / (64.0 / fd->get_font_scale(gl.font_size)); + gl.advance = Math::round(glyph_pos[i].x_advance / (64.0 / fd->get_font_scale(fs))); } else { - gl.advance = -glyph_pos[i].y_advance / (64.0 / fd->get_font_scale(gl.font_size)); + gl.advance = -Math::round(glyph_pos[i].y_advance / (64.0 / fd->get_font_scale(fs))); } - gl.x_off = glyph_pos[i].x_offset / (64.0 / fd->get_font_scale(gl.font_size)); - gl.y_off = -glyph_pos[i].y_offset / (64.0 / fd->get_font_scale(gl.font_size)); + gl.x_off = Math::round(glyph_pos[i].x_offset / (64.0 / fd->get_font_scale(fs))); + gl.y_off = -Math::round(glyph_pos[i].y_offset / (64.0 / fd->get_font_scale(fs))); } if (p_sd->preserve_control) { @@ -2026,14 +2074,12 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star } for (int j = 0; j < w[i].count; j++) { if (p_sd->orientation == ORIENTATION_HORIZONTAL) { - p_sd->ascent = MAX(p_sd->ascent, MAX(fd->get_ascent(w[i + j].font_size), w[i + j].y_off)); - p_sd->descent = MAX(p_sd->descent, MAX(fd->get_descent(w[i + j].font_size), w[i + j].y_off)); + p_sd->ascent = MAX(p_sd->ascent, -w[i + j].y_off); + p_sd->descent = MAX(p_sd->descent, w[i + j].y_off); } else { - p_sd->ascent = MAX(p_sd->ascent, fd->get_advance(w[i + j].index, w[i + j].font_size).x * 0.5); - p_sd->descent = MAX(p_sd->descent, fd->get_advance(w[i + j].index, w[i + j].font_size).x * 0.5); + p_sd->ascent = MAX(p_sd->ascent, Math::round(fd->get_advance(w[i + j].index, fs).x * 0.5)); + p_sd->descent = MAX(p_sd->descent, Math::round(fd->get_advance(w[i + j].index, fs).x * 0.5)); } - p_sd->upos = MAX(p_sd->upos, font_get_underline_position(w[i + j].font_rid, w[i + j].font_size)); - p_sd->uthk = MAX(p_sd->uthk, font_get_underline_thickness(w[i + j].font_rid, w[i + j].font_size)); p_sd->width += w[i + j].advance; p_sd->glyphs.push_back(w[i + j]); } @@ -2051,6 +2097,10 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star if (failed_subrun_start != p_end + 1) { _shape_run(p_sd, failed_subrun_start, failed_subrun_end, p_script, p_direction, p_fonts, p_span, p_fb_index + 1); } + p_sd->ascent = MAX(p_sd->ascent, fd->get_ascent(fs)); + p_sd->descent = MAX(p_sd->descent, fd->get_descent(fs)); + p_sd->upos = MAX(p_sd->upos, fd->get_underline_position(fs)); + p_sd->uthk = MAX(p_sd->uthk, fd->get_underline_thickness(fs)); } } @@ -2081,7 +2131,7 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) { sd->script_iter = memnew(ScriptIterator(sd->text, 0, sd->text.length())); } - if (sd->bidi_override.empty()) { + if (sd->bidi_override.is_empty()) { sd->bidi_override.push_back(Vector2i(0, sd->end)); } @@ -2180,8 +2230,8 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) { sd->ascent = MAX(sd->ascent, sd->objects[span.embedded_key].rect.size.y); } break; case VALIGN_CENTER: { - sd->ascent = MAX(sd->ascent, sd->objects[span.embedded_key].rect.size.y / 2); - sd->descent = MAX(sd->descent, sd->objects[span.embedded_key].rect.size.y / 2); + sd->ascent = MAX(sd->ascent, Math::round(sd->objects[span.embedded_key].rect.size.y / 2)); + sd->descent = MAX(sd->descent, Math::round(sd->objects[span.embedded_key].rect.size.y / 2)); } break; case VALIGN_BOTTOM: { sd->descent = MAX(sd->descent, sd->objects[span.embedded_key].rect.size.y); @@ -2195,8 +2245,8 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) { sd->ascent = MAX(sd->ascent, sd->objects[span.embedded_key].rect.size.x); } break; case VALIGN_CENTER: { - sd->ascent = MAX(sd->ascent, sd->objects[span.embedded_key].rect.size.x / 2); - sd->descent = MAX(sd->descent, sd->objects[span.embedded_key].rect.size.x / 2); + sd->ascent = MAX(sd->ascent, Math::round(sd->objects[span.embedded_key].rect.size.x / 2)); + sd->descent = MAX(sd->descent, Math::round(sd->objects[span.embedded_key].rect.size.x / 2)); } break; case VALIGN_BOTTOM: { sd->descent = MAX(sd->descent, sd->objects[span.embedded_key].rect.size.x); @@ -2207,7 +2257,6 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) { gl.start = span.start; gl.end = span.end; gl.count = 1; - gl.index = 0; gl.flags = GRAPHEME_IS_VALID | GRAPHEME_IS_VIRTUAL; if (sd->orientation == ORIENTATION_HORIZONTAL) { gl.advance = sd->objects[span.embedded_key].rect.size.x; @@ -2507,9 +2556,11 @@ void TextServerAdvanced::register_server() { } TextServerAdvanced::TextServerAdvanced() { + hb_bmp_create_font_funcs(); } TextServerAdvanced::~TextServerAdvanced() { + hb_bmp_free_font_funcs(); u_cleanup(); #ifndef ICU_STATIC_DATA if (icu_data != nullptr) { diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index f26b87f67e..89fae477f9 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -138,6 +138,10 @@ public: virtual bool font_get_antialiased(RID p_font) const override; virtual Dictionary font_get_feature_list(RID p_font) const override; + virtual Dictionary font_get_variation_list(RID p_font) const override; + + virtual void font_set_variation(RID p_font, const String &p_name, double p_value) override; + virtual double font_get_variation(RID p_font, const String &p_name) const override; virtual void font_set_hinting(RID p_font, Hinting p_hinting) override; virtual Hinting font_get_hinting(RID p_font) const override; diff --git a/modules/text_server_fb/SCsub b/modules/text_server_fb/SCsub index 7650e27063..03eccbe7bd 100644 --- a/modules/text_server_fb/SCsub +++ b/modules/text_server_fb/SCsub @@ -9,4 +9,5 @@ env_text_server_fb.Append( "#thirdparty/freetype/include", ] ) + env_text_server_fb.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/text_server_fb/bitmap_font_fb.cpp b/modules/text_server_fb/bitmap_font_fb.cpp index 5ab8edbd71..5c691b7bbd 100644 --- a/modules/text_server_fb/bitmap_font_fb.cpp +++ b/modules/text_server_fb/bitmap_font_fb.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -198,7 +198,6 @@ Error BitmapFontDataFallback::load_from_memory(const uint8_t *p_data, size_t p_s chr.rect.position.y = c[2]; chr.rect.size.x = c[3]; chr.rect.size.y = c[4]; - chr.texture_idx = 0; if (c[7] < 0) { chr.advance.x = c[3]; } else { diff --git a/modules/text_server_fb/bitmap_font_fb.h b/modules/text_server_fb/bitmap_font_fb.h index 73e6d8f791..33401b85fa 100644 --- a/modules/text_server_fb/bitmap_font_fb.h +++ b/modules/text_server_fb/bitmap_font_fb.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/text_server_fb/dynamic_font_fb.cpp b/modules/text_server_fb/dynamic_font_fb.cpp index 6feeaec102..4eecba6ae8 100644 --- a/modules/text_server_fb/dynamic_font_fb.cpp +++ b/modules/text_server_fb/dynamic_font_fb.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,8 +33,6 @@ #include FT_STROKER_H #include FT_ADVANCES_H -HashMap<String, Vector<uint8_t>> DynamicFontDataFallback::font_mem_cache; - DynamicFontDataFallback::DataAtSize *DynamicFontDataFallback::get_data_for_size(int p_size, int p_outline_size) { ERR_FAIL_COND_V(!valid, nullptr); ERR_FAIL_COND_V(p_size < 0 || p_size > UINT16_MAX, nullptr); @@ -55,11 +53,10 @@ DynamicFontDataFallback::DataAtSize *DynamicFontDataFallback::get_data_for_size( if (E != nullptr) { fds = E->get(); } else { - // FT_OPEN_STREAM is extremely slow only on Android. - if (OS::get_singleton()->get_name() == "Android" && font_mem == nullptr && font_path != String()) { - if (font_mem_cache.has(font_path)) { - font_mem = font_mem_cache[font_path].ptr(); - font_mem_size = font_mem_cache[font_path].size(); + if (font_mem == nullptr && font_path != String()) { + if (!font_mem_cache.is_empty()) { + font_mem = font_mem_cache.ptr(); + font_mem_size = font_mem_cache.size(); } else { FileAccess *f = FileAccess::open(font_path, FileAccess::READ); if (!f) { @@ -67,11 +64,9 @@ DynamicFontDataFallback::DataAtSize *DynamicFontDataFallback::get_data_for_size( } size_t len = f->get_len(); - font_mem_cache[font_path] = Vector<uint8_t>(); - Vector<uint8_t> &fontdata = font_mem_cache[font_path]; - fontdata.resize(len); - f->get_buffer(fontdata.ptrw(), len); - font_mem = fontdata.ptr(); + font_mem_cache.resize(len); + f->get_buffer(font_mem_cache.ptrw(), len); + font_mem = font_mem_cache.ptr(); font_mem_size = len; f->close(); } @@ -79,27 +74,7 @@ DynamicFontDataFallback::DataAtSize *DynamicFontDataFallback::get_data_for_size( int error = 0; fds = memnew(DataAtSize); - if (font_mem == nullptr && font_path != String()) { - FileAccess *f = FileAccess::open(font_path, FileAccess::READ); - if (!f) { - memdelete(fds); - ERR_FAIL_V_MSG(nullptr, "Cannot open font file '" + font_path + "'."); - } - - memset(&fds->stream, 0, sizeof(FT_StreamRec)); - fds->stream.base = nullptr; - fds->stream.size = f->get_len(); - fds->stream.pos = 0; - fds->stream.descriptor.pointer = f; - fds->stream.read = _ft_stream_io; - fds->stream.close = _ft_stream_close; - - FT_Open_Args fargs; - memset(&fargs, 0, sizeof(FT_Open_Args)); - fargs.flags = FT_OPEN_STREAM; - fargs.stream = &fds->stream; - error = FT_Open_Face(library, &fargs, 0, &fds->face); - } else if (font_mem) { + if (font_mem) { memset(&fds->stream, 0, sizeof(FT_StreamRec)); fds->stream.base = (unsigned char *)font_mem; fds->stream.size = font_mem_size; @@ -161,30 +136,9 @@ DynamicFontDataFallback::DataAtSize *DynamicFontDataFallback::get_data_for_size( return fds; } -unsigned long DynamicFontDataFallback::_ft_stream_io(FT_Stream stream, unsigned long offset, unsigned char *buffer, unsigned long count) { - FileAccess *f = (FileAccess *)stream->descriptor.pointer; - - if (f->get_position() != offset) { - f->seek(offset); - } - if (count == 0) { - return 0; - } - - return f->get_buffer(buffer, count); -} - -void DynamicFontDataFallback::_ft_stream_close(FT_Stream stream) { - FileAccess *f = (FileAccess *)stream->descriptor.pointer; - f->close(); - memdelete(f); -} - DynamicFontDataFallback::TexturePosition DynamicFontDataFallback::find_texture_pos_for_glyph(DynamicFontDataFallback::DataAtSize *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height) { TexturePosition ret; ret.index = -1; - ret.x = 0; - ret.y = 0; int mw = p_width; int mh = p_height; @@ -363,8 +317,8 @@ DynamicFontDataFallback::Character DynamicFontDataFallback::bitmap_to_character( } Character chr; - chr.align = Vector2(xofs, -yofs) * p_data->scale_color_font / oversampling; - chr.advance = advance * p_data->scale_color_font / oversampling; + chr.align = (Vector2(xofs, -yofs) * p_data->scale_color_font / oversampling).round(); + chr.advance = (advance * p_data->scale_color_font / oversampling).round(); chr.texture_idx = tex_pos.index; chr.found = true; diff --git a/modules/text_server_fb/dynamic_font_fb.h b/modules/text_server_fb/dynamic_font_fb.h index 060b8cfbf9..f1cd758f2c 100644 --- a/modules/text_server_fb/dynamic_font_fb.h +++ b/modules/text_server_fb/dynamic_font_fb.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -107,7 +107,7 @@ private: const uint8_t *font_mem = nullptr; int font_mem_size = 0; String font_path; - static HashMap<String, Vector<uint8_t>> font_mem_cache; + Vector<uint8_t> font_mem_cache; float rect_margin = 1.f; int base_size = 16; @@ -119,9 +119,6 @@ private: Map<CacheID, DataAtSize *> size_cache; Map<CacheID, DataAtSize *> size_cache_outline; - static unsigned long _ft_stream_io(FT_Stream stream, unsigned long offset, unsigned char *buffer, unsigned long count); - static void _ft_stream_close(FT_Stream stream); - DataAtSize *get_data_for_size(int p_size, int p_outline_size = 0); TexturePosition find_texture_pos_for_glyph(DataAtSize *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height); diff --git a/modules/text_server_fb/font_fb.h b/modules/text_server_fb/font_fb.h index d2ce2661a1..cc72919542 100644 --- a/modules/text_server_fb/font_fb.h +++ b/modules/text_server_fb/font_fb.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/text_server_fb/register_types.cpp b/modules/text_server_fb/register_types.cpp index ad4d2d47ab..87cbd2ac2c 100644 --- a/modules/text_server_fb/register_types.cpp +++ b/modules/text_server_fb/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/text_server_fb/register_types.h b/modules/text_server_fb/register_types.h index 58f8436c67..c854db92e5 100644 --- a/modules/text_server_fb/register_types.h +++ b/modules/text_server_fb/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index e74a1d9ef9..e60d269408 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -45,6 +45,10 @@ _FORCE_INLINE_ bool is_linebreak(char32_t p_char) { return (p_char >= 0x000a && p_char <= 0x000d) || (p_char == 0x0085) || (p_char == 0x2028) || (p_char == 0x2029); } +_FORCE_INLINE_ bool is_punct(char32_t p_char) { + return (p_char >= 0x0020 && p_char <= 0x002F) || (p_char >= 0x003A && p_char <= 0x0040) || (p_char >= 0x005B && p_char <= 0x0060) || (p_char >= 0x007B && p_char <= 0x007E) || (p_char >= 0x2000 && p_char <= 0x206F) || (p_char >= 0x3000 && p_char <= 0x303F); +} + /*************************************************************************/ String TextServerFallback::interface_name = "Fallback"; @@ -546,7 +550,7 @@ bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_te ERR_FAIL_COND_V(!sd, false); ERR_FAIL_COND_V(p_size <= 0, false); - if (p_text.empty()) { + if (p_text.is_empty()) { return true; } @@ -569,7 +573,7 @@ bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_te span.fonts.push_back(p_fonts[i]); } } - ERR_FAIL_COND_V(span.fonts.empty(), false); + ERR_FAIL_COND_V(span.fonts.is_empty(), false); span.font_size = p_size; span.language = p_language; @@ -625,7 +629,11 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, sd->width = 0; sd->upos = 0; sd->uthk = 0; - for (int i = 0; i < sd->glyphs.size(); i++) { + int sd_size = sd->glyphs.size(); + const FontDataFallback *fd = nullptr; + RID prev_rid = RID(); + + for (int i = 0; i < sd_size; i++) { Glyph gl = sd->glyphs[i]; Variant key; if (gl.count == 1) { @@ -645,8 +653,8 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, sd->ascent = MAX(sd->ascent, sd->objects[key].rect.size.y); } break; case VALIGN_CENTER: { - sd->ascent = MAX(sd->ascent, sd->objects[key].rect.size.y / 2); - sd->descent = MAX(sd->descent, sd->objects[key].rect.size.y / 2); + sd->ascent = MAX(sd->ascent, Math::round(sd->objects[key].rect.size.y / 2)); + sd->descent = MAX(sd->descent, Math::round(sd->objects[key].rect.size.y / 2)); } break; case VALIGN_BOTTOM: { sd->descent = MAX(sd->descent, sd->objects[key].rect.size.y); @@ -661,8 +669,8 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, sd->ascent = MAX(sd->ascent, sd->objects[key].rect.size.x); } break; case VALIGN_CENTER: { - sd->ascent = MAX(sd->ascent, sd->objects[key].rect.size.x / 2); - sd->descent = MAX(sd->descent, sd->objects[key].rect.size.x / 2); + sd->ascent = MAX(sd->ascent, Math::round(sd->objects[key].rect.size.x / 2)); + sd->descent = MAX(sd->descent, Math::round(sd->objects[key].rect.size.x / 2)); } break; case VALIGN_BOTTOM: { sd->descent = MAX(sd->descent, sd->objects[key].rect.size.x); @@ -671,25 +679,28 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, sd->glyphs.write[i].advance = sd->objects[key].rect.size.y; } } else { - const FontDataFallback *fd = font_owner.getornull(gl.font_rid); + if (prev_rid != gl.font_rid) { + fd = font_owner.getornull(gl.font_rid); + prev_rid = gl.font_rid; + } if (fd != nullptr) { if (sd->orientation == ORIENTATION_HORIZONTAL) { sd->ascent = MAX(sd->ascent, fd->get_ascent(gl.font_size)); sd->descent = MAX(sd->descent, fd->get_descent(gl.font_size)); } else { - sd->ascent = MAX(sd->ascent, fd->get_advance(gl.index, gl.font_size).x * 0.5); - sd->descent = MAX(sd->descent, fd->get_advance(gl.index, gl.font_size).x * 0.5); + sd->ascent = MAX(sd->ascent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5)); + sd->descent = MAX(sd->descent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5)); } sd->upos = MAX(sd->upos, font_get_underline_position(gl.font_rid, gl.font_size)); sd->uthk = MAX(sd->uthk, font_get_underline_thickness(gl.font_rid, gl.font_size)); } else if (sd->preserve_invalid || (sd->preserve_control && is_control(gl.index))) { // Glyph not found, replace with hex code box. if (sd->orientation == ORIENTATION_HORIZONTAL) { - sd->ascent = MAX(sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f); - sd->descent = MAX(sd->descent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f); + sd->ascent = MAX(sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f)); + sd->descent = MAX(sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f)); } else { - sd->ascent = MAX(sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f); - sd->descent = MAX(sd->descent, get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f); + sd->ascent = MAX(sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f)); + sd->descent = MAX(sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f)); } } sd->width += gl.advance * gl.repeat; @@ -760,21 +771,25 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng if (p_length > 0) { new_sd->text = sd->text.substr(p_start, p_length); + int sd_size = sd->glyphs.size(); + const Glyph *sd_glyphs = sd->glyphs.ptr(); - for (int i = 0; i < sd->glyphs.size(); i++) { - if ((sd->glyphs[i].start >= new_sd->start) && (sd->glyphs[i].end <= new_sd->end)) { - Glyph gl = sd->glyphs[i]; + for (int i = 0; i < sd_size; i++) { + if ((sd_glyphs[i].start >= new_sd->start) && (sd_glyphs[i].end <= new_sd->end)) { + Glyph gl = sd_glyphs[i]; Variant key; + bool find_embedded = false; if (gl.count == 1) { for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) { if (E->get().pos == gl.start) { + find_embedded = true; key = E->key(); new_sd->objects[key] = E->get(); break; } } } - if (key != Variant()) { + if (find_embedded) { if (new_sd->orientation == ORIENTATION_HORIZONTAL) { new_sd->objects[key].rect.position.x = new_sd->width; new_sd->width += new_sd->objects[key].rect.size.x; @@ -783,8 +798,8 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng new_sd->ascent = MAX(new_sd->ascent, new_sd->objects[key].rect.size.y); } break; case VALIGN_CENTER: { - new_sd->ascent = MAX(new_sd->ascent, new_sd->objects[key].rect.size.y / 2); - new_sd->descent = MAX(new_sd->descent, new_sd->objects[key].rect.size.y / 2); + new_sd->ascent = MAX(new_sd->ascent, Math::round(new_sd->objects[key].rect.size.y / 2)); + new_sd->descent = MAX(new_sd->descent, Math::round(new_sd->objects[key].rect.size.y / 2)); } break; case VALIGN_BOTTOM: { new_sd->descent = MAX(new_sd->descent, new_sd->objects[key].rect.size.y); @@ -798,8 +813,8 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng new_sd->ascent = MAX(new_sd->ascent, new_sd->objects[key].rect.size.x); } break; case VALIGN_CENTER: { - new_sd->ascent = MAX(new_sd->ascent, new_sd->objects[key].rect.size.x / 2); - new_sd->descent = MAX(new_sd->descent, new_sd->objects[key].rect.size.x / 2); + new_sd->ascent = MAX(new_sd->ascent, Math::round(new_sd->objects[key].rect.size.x / 2)); + new_sd->descent = MAX(new_sd->descent, Math::round(new_sd->objects[key].rect.size.x / 2)); } break; case VALIGN_BOTTOM: { new_sd->descent = MAX(new_sd->descent, new_sd->objects[key].rect.size.x); @@ -813,17 +828,17 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng new_sd->ascent = MAX(new_sd->ascent, fd->get_ascent(gl.font_size)); new_sd->descent = MAX(new_sd->descent, fd->get_descent(gl.font_size)); } else { - new_sd->ascent = MAX(new_sd->ascent, fd->get_advance(gl.index, gl.font_size).x * 0.5); - new_sd->descent = MAX(new_sd->descent, fd->get_advance(gl.index, gl.font_size).x * 0.5); + new_sd->ascent = MAX(new_sd->ascent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5)); + new_sd->descent = MAX(new_sd->descent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5)); } } else if (new_sd->preserve_invalid || (new_sd->preserve_control && is_control(gl.index))) { // Glyph not found, replace with hex code box. if (new_sd->orientation == ORIENTATION_HORIZONTAL) { - new_sd->ascent = MAX(new_sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f); - new_sd->descent = MAX(new_sd->descent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f); + new_sd->ascent = MAX(new_sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f)); + new_sd->descent = MAX(new_sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f)); } else { - new_sd->ascent = MAX(new_sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f); - new_sd->descent = MAX(new_sd->descent, get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f); + new_sd->ascent = MAX(new_sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f)); + new_sd->descent = MAX(new_sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f)); } } new_sd->width += gl.advance * gl.repeat; @@ -943,7 +958,7 @@ float TextServerFallback::shaped_text_fit_to_width(RID p_shaped, float p_width, if (gl.count > 0) { if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE) { float old_adv = gl.advance; - gl.advance = MAX(gl.advance + delta_width_per_space, 0.05 * gl.font_size); + gl.advance = Math::round(MAX(gl.advance + delta_width_per_space, 0.05 * gl.font_size)); sd->width += (gl.advance - old_adv); } } @@ -978,8 +993,10 @@ float TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vector<float delta = -1; } + Glyph *gl = sd->glyphs.ptrw(); + for (int i = start; i != end; i += delta) { - if ((sd->glyphs[i].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB) { + if ((gl[i].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB) { float tab_off = 0.f; while (tab_off <= off) { tab_off += p_tab_stops[tab_index]; @@ -988,13 +1005,13 @@ float TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vector<float tab_index = 0; } } - float old_adv = sd->glyphs.write[i].advance; - sd->glyphs.write[i].advance = (tab_off - off); - sd->width += sd->glyphs.write[i].advance - old_adv; + float old_adv = gl[i].advance; + gl[i].advance = tab_off - off; + sd->width += gl[i].advance - old_adv; off = 0; continue; } - off += sd->glyphs[i].advance * sd->glyphs[i].repeat; + off += gl[i].advance * gl[i].repeat; } return 0.f; @@ -1012,9 +1029,13 @@ bool TextServerFallback::shaped_text_update_breaks(RID p_shaped) { return true; // Noting to do. } - for (int i = 0; i < sd->glyphs.size(); i++) { + int sd_size = sd->glyphs.size(); + for (int i = 0; i < sd_size; i++) { if (sd->glyphs[i].count > 0) { char32_t c = sd->text[sd->glyphs[i].start]; + if (is_punct(c)) { + sd->glyphs.write[i].flags |= GRAPHEME_IS_PUNCTUATION; + } if (is_whitespace(c) && !is_linebreak(c)) { sd->glyphs.write[i].flags |= GRAPHEME_IS_SPACE; sd->glyphs.write[i].flags |= GRAPHEME_IS_BREAK_SOFT; @@ -1086,8 +1107,8 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) { sd->ascent = MAX(sd->ascent, sd->objects[span.embedded_key].rect.size.y); } break; case VALIGN_CENTER: { - sd->ascent = MAX(sd->ascent, sd->objects[span.embedded_key].rect.size.y / 2); - sd->descent = MAX(sd->descent, sd->objects[span.embedded_key].rect.size.y / 2); + sd->ascent = MAX(sd->ascent, Math::round(sd->objects[span.embedded_key].rect.size.y / 2)); + sd->descent = MAX(sd->descent, Math::round(sd->objects[span.embedded_key].rect.size.y / 2)); } break; case VALIGN_BOTTOM: { sd->descent = MAX(sd->descent, sd->objects[span.embedded_key].rect.size.y); @@ -1101,8 +1122,8 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) { sd->ascent = MAX(sd->ascent, sd->objects[span.embedded_key].rect.size.x); } break; case VALIGN_CENTER: { - sd->ascent = MAX(sd->ascent, sd->objects[span.embedded_key].rect.size.x / 2); - sd->descent = MAX(sd->descent, sd->objects[span.embedded_key].rect.size.x / 2); + sd->ascent = MAX(sd->ascent, Math::round(sd->objects[span.embedded_key].rect.size.x / 2)); + sd->descent = MAX(sd->descent, Math::round(sd->objects[span.embedded_key].rect.size.x / 2)); } break; case VALIGN_BOTTOM: { sd->descent = MAX(sd->descent, sd->objects[span.embedded_key].rect.size.x); @@ -1157,14 +1178,14 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) { sd->descent = MAX(sd->descent, fd->get_descent(gl.font_size)); } else { gl.advance = fd->get_advance(gl.index, gl.font_size).y; - gl.x_off = -fd->get_advance(gl.index, gl.font_size).x * 0.5; + gl.x_off = -Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5); gl.y_off = fd->get_ascent(gl.font_size); - sd->ascent = MAX(sd->ascent, fd->get_advance(gl.index, gl.font_size).x * 0.5); - sd->descent = MAX(sd->descent, fd->get_advance(gl.index, gl.font_size).x * 0.5); + sd->ascent = MAX(sd->ascent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5)); + sd->descent = MAX(sd->descent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5)); } } - sd->upos = MAX(sd->upos, font_get_underline_position(gl.font_rid, gl.font_size)); - sd->uthk = MAX(sd->uthk, font_get_underline_thickness(gl.font_rid, gl.font_size)); + sd->upos = MAX(sd->upos, fd->get_underline_position(gl.font_size)); + sd->uthk = MAX(sd->uthk, fd->get_underline_thickness(gl.font_size)); // Add kerning to previous glyph. if (sd->glyphs.size() > 0) { @@ -1181,12 +1202,12 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) { // Glyph not found, replace with hex code box. if (sd->orientation == ORIENTATION_HORIZONTAL) { gl.advance = get_hex_code_box_size(gl.font_size, gl.index).x; - sd->ascent = MAX(sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f); - sd->descent = MAX(sd->descent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f); + sd->ascent = MAX(sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f)); + sd->descent = MAX(sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f)); } else { gl.advance = get_hex_code_box_size(gl.font_size, gl.index).y; - sd->ascent = MAX(sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f); - sd->descent = MAX(sd->descent, get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f); + sd->ascent = MAX(sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f)); + sd->descent = MAX(sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f)); } } sd->width += gl.advance; diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h index 56bb1f7bf9..d142b320e4 100644 --- a/modules/text_server_fb/text_server_fb.h +++ b/modules/text_server_fb/text_server_fb.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/tga/image_loader_tga.cpp b/modules/tga/image_loader_tga.cpp index aa8b3122f4..2da9159228 100644 --- a/modules/tga/image_loader_tga.cpp +++ b/modules/tga/image_loader_tga.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/tga/image_loader_tga.h b/modules/tga/image_loader_tga.h index 6b3d33e7ef..249e33411e 100644 --- a/modules/tga/image_loader_tga.h +++ b/modules/tga/image_loader_tga.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/tga/register_types.cpp b/modules/tga/register_types.cpp index 320f748083..9e5fe124ef 100644 --- a/modules/tga/register_types.cpp +++ b/modules/tga/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/tga/register_types.h b/modules/tga/register_types.h index 94a77d295e..0dcd750250 100644 --- a/modules/tga/register_types.h +++ b/modules/tga/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/theora/SCsub b/modules/theora/SCsub index a01e65b4b0..6038ea086a 100644 --- a/modules/theora/SCsub +++ b/modules/theora/SCsub @@ -6,6 +6,9 @@ Import("env_modules") env_theora = env_modules.Clone() # Thirdparty source files + +thirdparty_obj = [] + if env["builtin_libtheora"]: thirdparty_dir = "#thirdparty/libtheora/" thirdparty_sources = [ @@ -80,7 +83,16 @@ if env["builtin_libtheora"]: env_thirdparty = env_theora.Clone() env_thirdparty.disable_warnings() - env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) + env.modules_sources += thirdparty_obj + # Godot source files -env_theora.add_source_files(env.modules_sources, "*.cpp") + +module_obj = [] + +env_theora.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj + +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/theora/register_types.cpp b/modules/theora/register_types.cpp index 0676cab5c5..0218b8c7a4 100644 --- a/modules/theora/register_types.cpp +++ b/modules/theora/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/theora/register_types.h b/modules/theora/register_types.h index 4f0670b2c7..654d70e417 100644 --- a/modules/theora/register_types.h +++ b/modules/theora/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp index 4f33f58ed1..243265769e 100644 --- a/modules/theora/video_stream_theora.cpp +++ b/modules/theora/video_stream_theora.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/theora/video_stream_theora.h b/modules/theora/video_stream_theora.h index 867f464038..d2036b5cb4 100644 --- a/modules/theora/video_stream_theora.h +++ b/modules/theora/video_stream_theora.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/tinyexr/SCsub b/modules/tinyexr/SCsub index 84b3b4015b..30bde96fb4 100644 --- a/modules/tinyexr/SCsub +++ b/modules/tinyexr/SCsub @@ -6,6 +6,9 @@ Import("env_modules") env_tinyexr = env_modules.Clone() # Thirdparty source files + +thirdparty_obj = [] + # Not unbundled for now as they are not commonly available as shared library thirdparty_dir = "#thirdparty/tinyexr/" thirdparty_sources = [ @@ -20,7 +23,15 @@ env_tinyexr.Append(CPPDEFINES=["TINYEXR_USE_THREAD"]) env_thirdparty = env_tinyexr.Clone() env_thirdparty.disable_warnings() -env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) +env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) +env.modules_sources += thirdparty_obj + +# Godot source files + +module_obj = [] + +env_tinyexr.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj -# Godot's own source files -env_tinyexr.add_source_files(env.modules_sources, "*.cpp") +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/tinyexr/image_loader_tinyexr.cpp b/modules/tinyexr/image_loader_tinyexr.cpp index 75dc16c39a..47214e6974 100644 --- a/modules/tinyexr/image_loader_tinyexr.cpp +++ b/modules/tinyexr/image_loader_tinyexr.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/tinyexr/image_loader_tinyexr.h b/modules/tinyexr/image_loader_tinyexr.h index ff040b0915..34390fccb0 100644 --- a/modules/tinyexr/image_loader_tinyexr.h +++ b/modules/tinyexr/image_loader_tinyexr.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/tinyexr/image_saver_tinyexr.cpp b/modules/tinyexr/image_saver_tinyexr.cpp index 420619bd5f..f747763248 100644 --- a/modules/tinyexr/image_saver_tinyexr.cpp +++ b/modules/tinyexr/image_saver_tinyexr.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/tinyexr/image_saver_tinyexr.h b/modules/tinyexr/image_saver_tinyexr.h index c7154bcfc7..e5060ef11c 100644 --- a/modules/tinyexr/image_saver_tinyexr.h +++ b/modules/tinyexr/image_saver_tinyexr.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/tinyexr/register_types.cpp b/modules/tinyexr/register_types.cpp index 9d0fb8729e..ecbabc4951 100644 --- a/modules/tinyexr/register_types.cpp +++ b/modules/tinyexr/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/tinyexr/register_types.h b/modules/tinyexr/register_types.h index 9739488312..e401f37066 100644 --- a/modules/tinyexr/register_types.h +++ b/modules/tinyexr/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/upnp/SCsub b/modules/upnp/SCsub index 2e129e15ca..bc0b215be3 100644 --- a/modules/upnp/SCsub +++ b/modules/upnp/SCsub @@ -7,6 +7,8 @@ env_upnp = env_modules.Clone() # Thirdparty source files +thirdparty_obj = [] + if env["builtin_miniupnpc"]: thirdparty_dir = "#thirdparty/miniupnpc/" thirdparty_sources = [ @@ -31,7 +33,16 @@ if env["builtin_miniupnpc"]: env_thirdparty = env_upnp.Clone() env_thirdparty.disable_warnings() - env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) + env.modules_sources += thirdparty_obj + # Godot source files -env_upnp.add_source_files(env.modules_sources, "*.cpp") + +module_obj = [] + +env_upnp.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj + +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/upnp/register_types.cpp b/modules/upnp/register_types.cpp index e66bc9d11a..a5ee39517f 100644 --- a/modules/upnp/register_types.cpp +++ b/modules/upnp/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/upnp/register_types.h b/modules/upnp/register_types.h index 0c71db9ffa..768031c4d9 100644 --- a/modules/upnp/register_types.h +++ b/modules/upnp/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/upnp/upnp.cpp b/modules/upnp/upnp.cpp index 988bf3017d..8c3be884bd 100644 --- a/modules/upnp/upnp.cpp +++ b/modules/upnp/upnp.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,7 +36,7 @@ #include <stdlib.h> bool UPNP::is_common_device(const String &dev) const { - return dev.empty() || + return dev.is_empty() || dev.find("InternetGatewayDevice") >= 0 || dev.find("WANIPConnection") >= 0 || dev.find("WANPPPConnection") >= 0 || @@ -76,7 +76,7 @@ int UPNP::discover(int timeout, int ttl, const String &device_filter) { struct UPNPDev *dev = devlist; while (dev) { - if (device_filter.empty() || strstr(dev->st, device_filter.utf8().get_data())) { + if (device_filter.is_empty() || strstr(dev->st, device_filter.utf8().get_data())) { add_device_to_list(dev, devlist); } diff --git a/modules/upnp/upnp.h b/modules/upnp/upnp.h index 81d770ec4c..9dfa907476 100644 --- a/modules/upnp/upnp.h +++ b/modules/upnp/upnp.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/upnp/upnp_device.cpp b/modules/upnp/upnp_device.cpp index 40eb6106a0..ddc66d593c 100644 --- a/modules/upnp/upnp_device.cpp +++ b/modules/upnp/upnp_device.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -65,7 +65,7 @@ int UPNPDevice::add_port_mapping(int port, int port_internal, String desc, Strin itos(port).utf8().get_data(), itos(port_internal).utf8().get_data(), igd_our_addr.utf8().get_data(), - desc.empty() ? nullptr : desc.utf8().get_data(), + desc.is_empty() ? nullptr : desc.utf8().get_data(), proto.utf8().get_data(), nullptr, // Remote host, always nullptr as IGDs don't support it duration > 0 ? itos(duration).utf8().get_data() : nullptr); diff --git a/modules/upnp/upnp_device.h b/modules/upnp/upnp_device.h index 53d621c90a..126e761a56 100644 --- a/modules/upnp/upnp_device.h +++ b/modules/upnp/upnp_device.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/vhacd/SCsub b/modules/vhacd/SCsub index ecd432b275..1ff4122114 100644 --- a/modules/vhacd/SCsub +++ b/modules/vhacd/SCsub @@ -7,6 +7,8 @@ env_vhacd = env_modules.Clone() # Thirdparty source files +thirdparty_obj = [] + thirdparty_dir = "#thirdparty/vhacd/" thirdparty_sources = [ @@ -24,10 +26,19 @@ thirdparty_sources = [ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env_vhacd.Prepend(CPPPATH=[thirdparty_dir + "/inc"]) +env_vhacd.Prepend(CPPPATH=[thirdparty_dir + "inc"]) env_thirdparty = env_vhacd.Clone() env_thirdparty.disable_warnings() -env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) +env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) +env.modules_sources += thirdparty_obj + +# Godot source files + +module_obj = [] + +env_vhacd.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj -env_vhacd.add_source_files(env.modules_sources, "*.cpp") +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/vhacd/register_types.cpp b/modules/vhacd/register_types.cpp index 40c5e47440..daad39bdfb 100644 --- a/modules/vhacd/register_types.cpp +++ b/modules/vhacd/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/vhacd/register_types.h b/modules/vhacd/register_types.h index d02a990901..24ad9378f4 100644 --- a/modules/vhacd/register_types.h +++ b/modules/vhacd/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/visual_script/doc_classes/VisualScript.xml b/modules/visual_script/doc_classes/VisualScript.xml index 088d84d2ec..5112ea43a7 100644 --- a/modules/visual_script/doc_classes/VisualScript.xml +++ b/modules/visual_script/doc_classes/VisualScript.xml @@ -26,23 +26,23 @@ </return> <argument index="0" name="name" type="StringName"> </argument> + <argument index="1" name="func_node_id" type="int"> + </argument> <description> - Add a function with the specified name to the VisualScript. + Add a function with the specified name to the VisualScript, and assign the root [VisualScriptFunction] node's id as [code]func_node_id[/code]. </description> </method> <method name="add_node"> <return type="void"> </return> - <argument index="0" name="func" type="StringName"> - </argument> - <argument index="1" name="id" type="int"> + <argument index="0" name="id" type="int"> </argument> - <argument index="2" name="node" type="VisualScriptNode"> + <argument index="1" name="node" type="VisualScriptNode"> </argument> - <argument index="3" name="position" type="Vector2" default="Vector2( 0, 0 )"> + <argument index="2" name="position" type="Vector2" default="Vector2( 0, 0 )"> </argument> <description> - Add a node to a function of the VisualScript. + Add a node to the VisualScript. </description> </method> <method name="add_variable"> @@ -157,15 +157,13 @@ <method name="data_connect"> <return type="void"> </return> - <argument index="0" name="func" type="StringName"> - </argument> - <argument index="1" name="from_node" type="int"> + <argument index="0" name="from_node" type="int"> </argument> - <argument index="2" name="from_port" type="int"> + <argument index="1" name="from_port" type="int"> </argument> - <argument index="3" name="to_node" type="int"> + <argument index="2" name="to_node" type="int"> </argument> - <argument index="4" name="to_port" type="int"> + <argument index="3" name="to_port" type="int"> </argument> <description> Connect two data ports. The value of [code]from_node[/code]'s [code]from_port[/code] would be fed into [code]to_node[/code]'s [code]to_port[/code]. @@ -174,15 +172,13 @@ <method name="data_disconnect"> <return type="void"> </return> - <argument index="0" name="func" type="StringName"> + <argument index="0" name="from_node" type="int"> </argument> - <argument index="1" name="from_node" type="int"> + <argument index="1" name="from_port" type="int"> </argument> - <argument index="2" name="from_port" type="int"> + <argument index="2" name="to_node" type="int"> </argument> - <argument index="3" name="to_node" type="int"> - </argument> - <argument index="4" name="to_port" type="int"> + <argument index="3" name="to_port" type="int"> </argument> <description> Disconnect two data ports previously connected with [method data_connect]. @@ -197,37 +193,31 @@ Returns the id of a function's entry point node. </description> </method> - <method name="get_function_scroll" qualifiers="const"> - <return type="Vector2"> - </return> - <argument index="0" name="name" type="StringName"> - </argument> - <description> - Returns the position of the center of the screen for a given function. - </description> - </method> <method name="get_node" qualifiers="const"> <return type="VisualScriptNode"> </return> - <argument index="0" name="func" type="StringName"> - </argument> - <argument index="1" name="id" type="int"> + <argument index="0" name="id" type="int"> </argument> <description> - Returns a node given its id and its function. + Returns a node given its id. </description> </method> <method name="get_node_position" qualifiers="const"> <return type="Vector2"> </return> - <argument index="0" name="func" type="StringName"> - </argument> - <argument index="1" name="id" type="int"> + <argument index="0" name="id" type="int"> </argument> <description> Returns a node's position in pixels. </description> </method> + <method name="get_scroll" qualifiers="const"> + <return type="Vector2"> + </return> + <description> + Returns the current position of the center of the screen. + </description> + </method> <method name="get_variable_default_value" qualifiers="const"> <return type="Variant"> </return> @@ -267,15 +257,13 @@ <method name="has_data_connection" qualifiers="const"> <return type="bool"> </return> - <argument index="0" name="func" type="StringName"> - </argument> - <argument index="1" name="from_node" type="int"> + <argument index="0" name="from_node" type="int"> </argument> - <argument index="2" name="from_port" type="int"> + <argument index="1" name="from_port" type="int"> </argument> - <argument index="3" name="to_node" type="int"> + <argument index="2" name="to_node" type="int"> </argument> - <argument index="4" name="to_port" type="int"> + <argument index="3" name="to_port" type="int"> </argument> <description> Returns whether the specified data ports are connected. @@ -293,9 +281,7 @@ <method name="has_node" qualifiers="const"> <return type="bool"> </return> - <argument index="0" name="func" type="StringName"> - </argument> - <argument index="1" name="id" type="int"> + <argument index="0" name="id" type="int"> </argument> <description> Returns whether a node exists with the given id. @@ -304,13 +290,11 @@ <method name="has_sequence_connection" qualifiers="const"> <return type="bool"> </return> - <argument index="0" name="func" type="StringName"> - </argument> - <argument index="1" name="from_node" type="int"> + <argument index="0" name="from_node" type="int"> </argument> - <argument index="2" name="from_output" type="int"> + <argument index="1" name="from_output" type="int"> </argument> - <argument index="3" name="to_node" type="int"> + <argument index="2" name="to_node" type="int"> </argument> <description> Returns whether the specified sequence ports are connected. @@ -346,12 +330,10 @@ <method name="remove_node"> <return type="void"> </return> - <argument index="0" name="func" type="StringName"> - </argument> - <argument index="1" name="id" type="int"> + <argument index="0" name="id" type="int"> </argument> <description> - Remove a specific node. + Remove the node with the specified id. </description> </method> <method name="remove_variable"> @@ -399,13 +381,11 @@ <method name="sequence_connect"> <return type="void"> </return> - <argument index="0" name="func" type="StringName"> + <argument index="0" name="from_node" type="int"> </argument> - <argument index="1" name="from_node" type="int"> + <argument index="1" name="from_output" type="int"> </argument> - <argument index="2" name="from_output" type="int"> - </argument> - <argument index="3" name="to_node" type="int"> + <argument index="2" name="to_node" type="int"> </argument> <description> Connect two sequence ports. The execution will flow from of [code]from_node[/code]'s [code]from_output[/code] into [code]to_node[/code]. @@ -415,29 +395,16 @@ <method name="sequence_disconnect"> <return type="void"> </return> - <argument index="0" name="func" type="StringName"> - </argument> - <argument index="1" name="from_node" type="int"> + <argument index="0" name="from_node" type="int"> </argument> - <argument index="2" name="from_output" type="int"> + <argument index="1" name="from_output" type="int"> </argument> - <argument index="3" name="to_node" type="int"> + <argument index="2" name="to_node" type="int"> </argument> <description> Disconnect two sequence ports previously connected with [method sequence_connect]. </description> </method> - <method name="set_function_scroll"> - <return type="void"> - </return> - <argument index="0" name="name" type="StringName"> - </argument> - <argument index="1" name="ofs" type="Vector2"> - </argument> - <description> - Position the center of the screen for a function. - </description> - </method> <method name="set_instance_base_type"> <return type="void"> </return> @@ -450,14 +417,21 @@ <method name="set_node_position"> <return type="void"> </return> - <argument index="0" name="func" type="StringName"> + <argument index="0" name="id" type="int"> </argument> - <argument index="1" name="id" type="int"> + <argument index="1" name="position" type="Vector2"> </argument> - <argument index="2" name="position" type="Vector2"> + <description> + Set the node position in the VisualScript graph. + </description> + </method> + <method name="set_scroll"> + <return type="void"> + </return> + <argument index="0" name="ofs" type="Vector2"> </argument> <description> - Position a node on the screen. + Set the screen center to the given position. </description> </method> <method name="set_variable_default_value"> @@ -496,9 +470,7 @@ </methods> <signals> <signal name="node_ports_changed"> - <argument index="0" name="function" type="String"> - </argument> - <argument index="1" name="id" type="int"> + <argument index="0" name="id" type="int"> </argument> <description> Emitted when the ports of a node are changed. diff --git a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml index 000fbd0140..4004f1a04c 100644 --- a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml +++ b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml @@ -92,7 +92,7 @@ <constant name="MATH_STEP_DECIMALS" value="24" enum="BuiltinFunc"> Return the number of digit places after the decimal that the first non-zero digit occurs. </constant> - <constant name="MATH_STEPIFY" value="25" enum="BuiltinFunc"> + <constant name="MATH_SNAPPED" value="25" enum="BuiltinFunc"> Return the input snapped to a given step. </constant> <constant name="MATH_LERP" value="26" enum="BuiltinFunc"> diff --git a/modules/visual_script/register_types.cpp b/modules/visual_script/register_types.cpp index 0172f29923..4c7b66e368 100644 --- a/modules/visual_script/register_types.cpp +++ b/modules/visual_script/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/visual_script/register_types.h b/modules/visual_script/register_types.h index c18c2930b1..b02a93ebc1 100644 --- a/modules/visual_script/register_types.h +++ b/modules/visual_script/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp index b10d4523f2..fe92f59179 100644 --- a/modules/visual_script/visual_script.cpp +++ b/modules/visual_script/visual_script.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,7 +36,7 @@ #include "scene/main/node.h" #include "visual_script_nodes.h" -//used by editor, this is not really saved +// Used by editor, this is not really saved. void VisualScriptNode::set_breakpoint(bool p_breakpoint) { breakpoint = p_breakpoint; } @@ -55,8 +55,8 @@ void VisualScriptNode::set_default_input_value(int p_port, const Variant &p_valu default_input_values[p_port] = p_value; #ifdef TOOLS_ENABLED - for (Set<VisualScript *>::Element *E = scripts_used.front(); E; E = E->next()) { - E->get()->set_edited(true); + if (script_used.is_valid()) { + script_used->set_edited(true); } #endif } @@ -73,14 +73,14 @@ void VisualScriptNode::_set_default_input_values(Array p_values) { void VisualScriptNode::validate_input_default_values() { default_input_values.resize(MAX(default_input_values.size(), get_input_value_port_count())); //let it grow as big as possible, we don't want to lose values on resize - //actually validate on save + // Actually validate on save. for (int i = 0; i < get_input_value_port_count(); i++) { Variant::Type expected = get_input_value_port_info(i).type; if (expected == Variant::NIL || expected == default_input_values[i].get_type()) { continue; } else { - //not the same, reconvert + // Not the same, reconvert. Callable::CallError ce; Variant existing = default_input_values[i]; const Variant *existingp = &existing; @@ -94,7 +94,7 @@ void VisualScriptNode::validate_input_default_values() { } Array VisualScriptNode::_get_default_input_values() const { - //validate on save, since on load there is little info about this + // Validate on save, since on load there is little info about this. Array values = default_input_values; values.resize(get_input_value_port_count()); @@ -118,6 +118,8 @@ void VisualScriptNode::_bind_methods() { } VisualScriptNode::TypeGuess VisualScriptNode::guess_output_type(TypeGuess *p_inputs, int p_output) const { + ERR_FAIL_COND_V(get_output_value_port_count() <= p_output, TypeGuess()); + PropertyInfo pinfo = get_output_value_port_info(p_output); TypeGuess tg; @@ -131,11 +133,7 @@ VisualScriptNode::TypeGuess VisualScriptNode::guess_output_type(TypeGuess *p_inp } Ref<VisualScript> VisualScriptNode::get_visual_script() const { - if (scripts_used.size()) { - return Ref<VisualScript>(scripts_used.front()->get()); - } - - return Ref<VisualScript>(); + return script_used; } VisualScriptNode::VisualScriptNode() { @@ -165,13 +163,15 @@ VisualScriptNodeInstance::~VisualScriptNodeInstance() { } } -void VisualScript::add_function(const StringName &p_name) { +void VisualScript::add_function(const StringName &p_name, int p_func_node_id) { ERR_FAIL_COND(instances.size()); ERR_FAIL_COND(!String(p_name).is_valid_identifier()); ERR_FAIL_COND(functions.has(p_name)); + ERR_FAIL_COND(variables.has(p_name)); + ERR_FAIL_COND(custom_signals.has(p_name)); functions[p_name] = Function(); - functions[p_name].scroll = Vector2(-50, -100); + functions[p_name].func_id = p_func_node_id; } bool VisualScript::has_function(const StringName &p_name) const { @@ -182,11 +182,7 @@ void VisualScript::remove_function(const StringName &p_name) { ERR_FAIL_COND(instances.size()); ERR_FAIL_COND(!functions.has(p_name)); - for (Map<int, Function::NodeData>::Element *E = functions[p_name].nodes.front(); E; E = E->next()) { - E->get().node->disconnect("ports_changed", callable_mp(this, &VisualScript::_node_ports_changed)); - E->get().node->scripts_used.erase(this); - } - + // Let the editor handle the node removal. functions.erase(p_name); } @@ -207,53 +203,36 @@ void VisualScript::rename_function(const StringName &p_name, const StringName &p functions.erase(p_name); } -void VisualScript::set_function_scroll(const StringName &p_name, const Vector2 &p_scroll) { - ERR_FAIL_COND(!functions.has(p_name)); - functions[p_name].scroll = p_scroll; +void VisualScript::set_scroll(const Vector2 &p_scroll) { + scroll = p_scroll; } -Vector2 VisualScript::get_function_scroll(const StringName &p_name) const { - ERR_FAIL_COND_V(!functions.has(p_name), Vector2()); - return functions[p_name].scroll; +Vector2 VisualScript::get_scroll() const { + return scroll; } void VisualScript::get_function_list(List<StringName> *r_functions) const { - for (const Map<StringName, Function>::Element *E = functions.front(); E; E = E->next()) { - r_functions->push_back(E->key()); - } - - r_functions->sort_custom<StringName::AlphCompare>(); + functions.get_key_list(r_functions); + // r_functions->sort_custom<StringName::AlphCompare>(); // Don't force sorting. } int VisualScript::get_function_node_id(const StringName &p_name) const { ERR_FAIL_COND_V(!functions.has(p_name), -1); - return functions[p_name].function_id; + return functions[p_name].func_id; } void VisualScript::_node_ports_changed(int p_id) { - StringName function; - - for (Map<StringName, Function>::Element *E = functions.front(); E; E = E->next()) { - if (E->get().nodes.has(p_id)) { - function = E->key(); - break; - } - } - - ERR_FAIL_COND(function == StringName()); - - Function &func = functions[function]; - Ref<VisualScriptNode> vsn = func.nodes[p_id].node; + Ref<VisualScriptNode> vsn = nodes[p_id].node; vsn->validate_input_default_values(); - //must revalidate all the functions + // Must revalidate all the functions. { List<SequenceConnection> to_remove; - for (Set<SequenceConnection>::Element *E = func.sequence_connections.front(); E; E = E->next()) { + for (Set<SequenceConnection>::Element *E = sequence_connections.front(); E; E = E->next()) { if (E->get().from_node == p_id && E->get().from_output >= vsn->get_output_sequence_port_count()) { to_remove.push_back(E->get()); } @@ -263,7 +242,7 @@ void VisualScript::_node_ports_changed(int p_id) { } while (to_remove.size()) { - func.sequence_connections.erase(to_remove.front()->get()); + sequence_connections.erase(to_remove.front()->get()); to_remove.pop_front(); } } @@ -271,7 +250,7 @@ void VisualScript::_node_ports_changed(int p_id) { { List<DataConnection> to_remove; - for (Set<DataConnection>::Element *E = func.data_connections.front(); E; E = E->next()) { + for (Set<DataConnection>::Element *E = data_connections.front(); E; E = E->next()) { if (E->get().from_node == p_id && E->get().from_port >= vsn->get_output_value_port_count()) { to_remove.push_back(E->get()); } @@ -281,63 +260,47 @@ void VisualScript::_node_ports_changed(int p_id) { } while (to_remove.size()) { - func.data_connections.erase(to_remove.front()->get()); + data_connections.erase(to_remove.front()->get()); to_remove.pop_front(); } } #ifdef TOOLS_ENABLED - set_edited(true); //something changed, let's set as edited - emit_signal("node_ports_changed", function, p_id); + set_edited(true); // Something changed, let's set as edited. + emit_signal("node_ports_changed", p_id); #endif } -void VisualScript::add_node(const StringName &p_func, int p_id, const Ref<VisualScriptNode> &p_node, const Point2 &p_pos) { +void VisualScript::add_node(int p_id, const Ref<VisualScriptNode> &p_node, const Point2 &p_pos) { ERR_FAIL_COND(instances.size()); - ERR_FAIL_COND(!functions.has(p_func)); - - for (Map<StringName, Function>::Element *E = functions.front(); E; E = E->next()) { - ERR_FAIL_COND(E->get().nodes.has(p_id)); //id can exist only one in script, even for different functions - } + ERR_FAIL_COND(nodes.has(p_id)); // ID can exist only one in script. - Function &func = functions[p_func]; - - if (Object::cast_to<VisualScriptFunction>(*p_node)) { - //the function indeed - ERR_FAIL_COND_MSG(func.function_id >= 0, "A function node has already been set here."); - - func.function_id = p_id; - } - - Function::NodeData nd; + NodeData nd; nd.node = p_node; nd.pos = p_pos; Ref<VisualScriptNode> vsn = p_node; vsn->connect("ports_changed", callable_mp(this, &VisualScript::_node_ports_changed), varray(p_id)); - vsn->scripts_used.insert(this); - vsn->validate_input_default_values(); // Validate when fully loaded + vsn->script_used = Ref<VisualScript>(this); + vsn->validate_input_default_values(); // Validate when fully loaded. - func.nodes[p_id] = nd; + nodes[p_id] = nd; } -void VisualScript::remove_node(const StringName &p_func, int p_id) { +void VisualScript::remove_node(int p_id) { ERR_FAIL_COND(instances.size()); - ERR_FAIL_COND(!functions.has(p_func)); - Function &func = functions[p_func]; - - ERR_FAIL_COND(!func.nodes.has(p_id)); + ERR_FAIL_COND(!nodes.has(p_id)); { List<SequenceConnection> to_remove; - for (Set<SequenceConnection>::Element *E = func.sequence_connections.front(); E; E = E->next()) { + for (Set<SequenceConnection>::Element *E = sequence_connections.front(); E; E = E->next()) { if (E->get().from_node == p_id || E->get().to_node == p_id) { to_remove.push_back(E->get()); } } while (to_remove.size()) { - func.sequence_connections.erase(to_remove.front()->get()); + sequence_connections.erase(to_remove.front()->get()); to_remove.pop_front(); } } @@ -345,122 +308,88 @@ void VisualScript::remove_node(const StringName &p_func, int p_id) { { List<DataConnection> to_remove; - for (Set<DataConnection>::Element *E = func.data_connections.front(); E; E = E->next()) { + for (Set<DataConnection>::Element *E = data_connections.front(); E; E = E->next()) { if (E->get().from_node == p_id || E->get().to_node == p_id) { to_remove.push_back(E->get()); } } while (to_remove.size()) { - func.data_connections.erase(to_remove.front()->get()); + data_connections.erase(to_remove.front()->get()); to_remove.pop_front(); } } - if (Object::cast_to<VisualScriptFunction>(func.nodes[p_id].node.ptr())) { - func.function_id = -1; //revert to invalid - } + nodes[p_id].node->disconnect("ports_changed", callable_mp(this, &VisualScript::_node_ports_changed)); + nodes[p_id].node->script_used.unref(); - func.nodes[p_id].node->disconnect("ports_changed", callable_mp(this, &VisualScript::_node_ports_changed)); - func.nodes[p_id].node->scripts_used.erase(this); - - func.nodes.erase(p_id); + nodes.erase(p_id); } -bool VisualScript::has_node(const StringName &p_func, int p_id) const { - ERR_FAIL_COND_V(!functions.has(p_func), false); - const Function &func = functions[p_func]; - - return func.nodes.has(p_id); +bool VisualScript::has_node(int p_id) const { + return nodes.has(p_id); } -Ref<VisualScriptNode> VisualScript::get_node(const StringName &p_func, int p_id) const { - ERR_FAIL_COND_V(!functions.has(p_func), Ref<VisualScriptNode>()); - const Function &func = functions[p_func]; - - ERR_FAIL_COND_V(!func.nodes.has(p_id), Ref<VisualScriptNode>()); +Ref<VisualScriptNode> VisualScript::get_node(int p_id) const { + ERR_FAIL_COND_V(!nodes.has(p_id), Ref<VisualScriptNode>()); - return func.nodes[p_id].node; + return nodes[p_id].node; } -void VisualScript::set_node_position(const StringName &p_func, int p_id, const Point2 &p_pos) { +void VisualScript::set_node_position(int p_id, const Point2 &p_pos) { ERR_FAIL_COND(instances.size()); - ERR_FAIL_COND(!functions.has(p_func)); - Function &func = functions[p_func]; - - ERR_FAIL_COND(!func.nodes.has(p_id)); - func.nodes[p_id].pos = p_pos; + ERR_FAIL_COND(!nodes.has(p_id)); + nodes[p_id].pos = p_pos; } -Point2 VisualScript::get_node_position(const StringName &p_func, int p_id) const { - ERR_FAIL_COND_V(!functions.has(p_func), Point2()); - const Function &func = functions[p_func]; - - ERR_FAIL_COND_V(!func.nodes.has(p_id), Point2()); - return func.nodes[p_id].pos; +Point2 VisualScript::get_node_position(int p_id) const { + ERR_FAIL_COND_V(!nodes.has(p_id), Point2()); + return nodes[p_id].pos; } -void VisualScript::get_node_list(const StringName &p_func, List<int> *r_nodes) const { - ERR_FAIL_COND(!functions.has(p_func)); - const Function &func = functions[p_func]; - - for (const Map<int, Function::NodeData>::Element *E = func.nodes.front(); E; E = E->next()) { - r_nodes->push_back(E->key()); - } +void VisualScript::get_node_list(List<int> *r_nodes) const { + nodes.get_key_list(r_nodes); } -void VisualScript::sequence_connect(const StringName &p_func, int p_from_node, int p_from_output, int p_to_node) { +void VisualScript::sequence_connect(int p_from_node, int p_from_output, int p_to_node) { ERR_FAIL_COND(instances.size()); - ERR_FAIL_COND(!functions.has(p_func)); - Function &func = functions[p_func]; SequenceConnection sc; sc.from_node = p_from_node; sc.from_output = p_from_output; sc.to_node = p_to_node; - ERR_FAIL_COND(func.sequence_connections.has(sc)); + ERR_FAIL_COND(sequence_connections.has(sc)); - func.sequence_connections.insert(sc); + sequence_connections.insert(sc); } -void VisualScript::sequence_disconnect(const StringName &p_func, int p_from_node, int p_from_output, int p_to_node) { - ERR_FAIL_COND(!functions.has(p_func)); - Function &func = functions[p_func]; - +void VisualScript::sequence_disconnect(int p_from_node, int p_from_output, int p_to_node) { SequenceConnection sc; sc.from_node = p_from_node; sc.from_output = p_from_output; sc.to_node = p_to_node; - ERR_FAIL_COND(!func.sequence_connections.has(sc)); + ERR_FAIL_COND(!sequence_connections.has(sc)); - func.sequence_connections.erase(sc); + sequence_connections.erase(sc); } -bool VisualScript::has_sequence_connection(const StringName &p_func, int p_from_node, int p_from_output, int p_to_node) const { - ERR_FAIL_COND_V(!functions.has(p_func), false); - const Function &func = functions[p_func]; - +bool VisualScript::has_sequence_connection(int p_from_node, int p_from_output, int p_to_node) const { SequenceConnection sc; sc.from_node = p_from_node; sc.from_output = p_from_output; sc.to_node = p_to_node; - return func.sequence_connections.has(sc); + return sequence_connections.has(sc); } -void VisualScript::get_sequence_connection_list(const StringName &p_func, List<SequenceConnection> *r_connection) const { - ERR_FAIL_COND(!functions.has(p_func)); - const Function &func = functions[p_func]; - - for (const Set<SequenceConnection>::Element *E = func.sequence_connections.front(); E; E = E->next()) { +void VisualScript::get_sequence_connection_list(List<SequenceConnection> *r_connection) const { + for (const Set<SequenceConnection>::Element *E = sequence_connections.front(); E; E = E->next()) { r_connection->push_back(E->get()); } } -void VisualScript::data_connect(const StringName &p_func, int p_from_node, int p_from_port, int p_to_node, int p_to_port) { +void VisualScript::data_connect(int p_from_node, int p_from_port, int p_to_node, int p_to_port) { ERR_FAIL_COND(instances.size()); - ERR_FAIL_COND(!functions.has(p_func)); - Function &func = functions[p_func]; DataConnection dc; dc.from_node = p_from_node; @@ -468,72 +397,55 @@ void VisualScript::data_connect(const StringName &p_func, int p_from_node, int p dc.to_node = p_to_node; dc.to_port = p_to_port; - ERR_FAIL_COND(func.data_connections.has(dc)); + ERR_FAIL_COND(data_connections.has(dc)); - func.data_connections.insert(dc); + data_connections.insert(dc); } -void VisualScript::data_disconnect(const StringName &p_func, int p_from_node, int p_from_port, int p_to_node, int p_to_port) { - ERR_FAIL_COND(!functions.has(p_func)); - Function &func = functions[p_func]; - +void VisualScript::data_disconnect(int p_from_node, int p_from_port, int p_to_node, int p_to_port) { DataConnection dc; dc.from_node = p_from_node; dc.from_port = p_from_port; dc.to_node = p_to_node; dc.to_port = p_to_port; - ERR_FAIL_COND(!func.data_connections.has(dc)); + ERR_FAIL_COND(!data_connections.has(dc)); - func.data_connections.erase(dc); + data_connections.erase(dc); } -bool VisualScript::has_data_connection(const StringName &p_func, int p_from_node, int p_from_port, int p_to_node, int p_to_port) const { - ERR_FAIL_COND_V(!functions.has(p_func), false); - const Function &func = functions[p_func]; - +bool VisualScript::has_data_connection(int p_from_node, int p_from_port, int p_to_node, int p_to_port) const { DataConnection dc; dc.from_node = p_from_node; dc.from_port = p_from_port; dc.to_node = p_to_node; dc.to_port = p_to_port; - return func.data_connections.has(dc); + return data_connections.has(dc); } -bool VisualScript::is_input_value_port_connected(const StringName &p_func, int p_node, int p_port) const { - ERR_FAIL_COND_V(!functions.has(p_func), false); - const Function &func = functions[p_func]; - - for (const Set<DataConnection>::Element *E = func.data_connections.front(); E; E = E->next()) { +bool VisualScript::is_input_value_port_connected(int p_node, int p_port) const { + for (const Set<DataConnection>::Element *E = data_connections.front(); E; E = E->next()) { if (E->get().to_node == p_node && E->get().to_port == p_port) { return true; } } - return false; } -bool VisualScript::get_input_value_port_connection_source(const StringName &p_func, int p_node, int p_port, int *r_node, int *r_port) const { - ERR_FAIL_COND_V(!functions.has(p_func), false); - const Function &func = functions[p_func]; - - for (const Set<DataConnection>::Element *E = func.data_connections.front(); E; E = E->next()) { +bool VisualScript::get_input_value_port_connection_source(int p_node, int p_port, int *r_node, int *r_port) const { + for (const Set<DataConnection>::Element *E = data_connections.front(); E; E = E->next()) { if (E->get().to_node == p_node && E->get().to_port == p_port) { *r_node = E->get().from_node; *r_port = E->get().from_port; return true; } } - return false; } -void VisualScript::get_data_connection_list(const StringName &p_func, List<DataConnection> *r_connection) const { - ERR_FAIL_COND(!functions.has(p_func)); - const Function &func = functions[p_func]; - - for (const Set<DataConnection>::Element *E = func.data_connections.front(); E; E = E->next()) { +void VisualScript::get_data_connection_list(List<DataConnection> *r_connection) const { + for (const Set<DataConnection>::Element *E = data_connections.front(); E; E = E->next()) { r_connection->push_back(E->get()); } } @@ -653,11 +565,8 @@ Dictionary VisualScript::_get_variable_info(const StringName &p_name) const { } void VisualScript::get_variable_list(List<StringName> *r_variables) const { - for (Map<StringName, Variable>::Element *E = variables.front(); E; E = E->next()) { - r_variables->push_back(E->key()); - } - - r_variables->sort_custom<StringName::AlphCompare>(); + variables.get_key_list(r_variables); + // r_variables->sort_custom<StringName::AlphCompare>(); // Don't force it. } void VisualScript::set_instance_base_type(const StringName &p_type) { @@ -680,24 +589,19 @@ void VisualScript::rename_variable(const StringName &p_name, const StringName &p variables[p_new_name] = variables[p_name]; variables.erase(p_name); - - List<StringName> funcs; - get_function_list(&funcs); - for (List<StringName>::Element *F = funcs.front(); F; F = F->next()) { // loop through all the functions - List<int> ids; - get_node_list(F->get(), &ids); - for (List<int>::Element *E = ids.front(); E; E = E->next()) { - Ref<VisualScriptVariableGet> nodeget = get_node(F->get(), E->get()); - if (nodeget.is_valid()) { - if (nodeget->get_variable() == p_name) { - nodeget->set_variable(p_new_name); - } - } else { - Ref<VisualScriptVariableSet> nodeset = get_node(F->get(), E->get()); - if (nodeset.is_valid()) { - if (nodeset->get_variable() == p_name) { - nodeset->set_variable(p_new_name); - } + List<int> ids; + get_node_list(&ids); + for (List<int>::Element *E = ids.front(); E; E = E->next()) { + Ref<VisualScriptVariableGet> nodeget = get_node(E->get()); + if (nodeget.is_valid()) { + if (nodeget->get_variable() == p_name) { + nodeget->set_variable(p_new_name); + } + } else { + Ref<VisualScriptVariableSet> nodeset = get_node(E->get()); + if (nodeset.is_valid()) { + if (nodeset->get_variable() == p_name) { + nodeset->set_variable(p_new_name); } } } @@ -808,23 +712,24 @@ void VisualScript::get_custom_signal_list(List<StringName> *r_custom_signals) co } int VisualScript::get_available_id() const { - int max_id = 0; - for (Map<StringName, Function>::Element *E = functions.front(); E; E = E->next()) { - if (E->get().nodes.empty()) { - continue; + // This is infinitely increasing, + // so one might want to implement a better solution, + // if the there is a case for huge number of nodes to be added to visual script. + List<int> nds; + nodes.get_key_list(&nds); + int max = -1; + for (const List<int>::Element *E = nds.front(); E; E = E->next()) { + if (E->get() > max) { + max = E->get(); } - - int last_id = E->get().nodes.back()->key(); - max_id = MAX(max_id, last_id + 1); } - - return max_id; + return (max + 1); } ///////////////////////////////// bool VisualScript::can_instance() const { - return true; //ScriptServer::is_scripting_enabled(); + return true; // ScriptServer::is_scripting_enabled(); } StringName VisualScript::get_instance_base_type() const { @@ -832,7 +737,7 @@ StringName VisualScript::get_instance_base_type() const { } Ref<Script> VisualScript::get_base_script() const { - return Ref<Script>(); // no inheritance in visual script + return Ref<Script>(); // No inheritance in visual script. } #ifdef TOOLS_ENABLED @@ -842,20 +747,23 @@ void VisualScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder) void VisualScript::_update_placeholders() { if (placeholders.size() == 0) { - return; //no bother if no placeholders + return; // No bother if no placeholders. } List<PropertyInfo> pinfo; Map<StringName, Variant> values; - for (Map<StringName, Variable>::Element *E = variables.front(); E; E = E->next()) { - if (!E->get()._export) { + List<StringName> keys; + variables.get_key_list(&keys); + + for (List<StringName>::Element *E = keys.front(); E; E = E->next()) { + if (!variables[E->get()]._export) { continue; } - PropertyInfo p = E->get().info; - p.name = String(E->key()); + PropertyInfo p = variables[E->get()].info; + p.name = String(E->get()); pinfo.push_back(p); - values[p.name] = E->get().default_value; + values[p.name] = variables[E->get()].default_value; } for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) { @@ -875,17 +783,18 @@ ScriptInstance *VisualScript::instance_create(Object *p_this) { List<PropertyInfo> pinfo; Map<StringName, Variant> values; - for (Map<StringName, Variable>::Element *E = variables.front(); E; E = E->next()) { - if (!E->get()._export) { + List<StringName> keys; + variables.get_key_list(&keys); + + for (const List<StringName>::Element *E = keys.front(); E; E = E->next()) { + if (!variables[E->get()]._export) continue; - } - PropertyInfo p = E->get().info; - p.name = String(E->key()); + PropertyInfo p = variables[E->get()].info; + p.name = String(E->get()); pinfo.push_back(p); - values[p.name] = E->get().default_value; + values[p.name] = variables[E->get()].default_value; } - sins->update(pinfo, values); return sins; @@ -928,7 +837,7 @@ bool VisualScript::is_tool() const { } bool VisualScript::is_valid() const { - return true; //always valid + return true; // Always valid. } ScriptLanguage *VisualScript::get_language() const { @@ -964,11 +873,14 @@ bool VisualScript::get_property_default_value(const StringName &p_property, Vari } void VisualScript::get_script_method_list(List<MethodInfo> *p_list) const { - for (Map<StringName, Function>::Element *E = functions.front(); E; E = E->next()) { + List<StringName> funcs; + functions.get_key_list(&funcs); + + for (List<StringName>::Element *E = funcs.front(); E; E = E->next()) { MethodInfo mi; - mi.name = E->key(); - if (E->get().function_id >= 0) { - Ref<VisualScriptFunction> func = E->get().nodes[E->get().function_id].node; + mi.name = E->get(); + if (functions[E->get()].func_id >= 0) { + Ref<VisualScriptFunction> func = nodes[functions[E->get()].func_id].node; if (func.is_valid()) { for (int i = 0; i < func->get_argument_count(); i++) { PropertyInfo arg; @@ -988,15 +900,15 @@ bool VisualScript::has_method(const StringName &p_method) const { } MethodInfo VisualScript::get_method_info(const StringName &p_method) const { - const Map<StringName, Function>::Element *E = functions.find(p_method); - if (!E) { + const Function funct = functions[p_method]; + if (funct.func_id == -1) { return MethodInfo(); } MethodInfo mi; - mi.name = E->key(); - if (E->get().function_id >= 0) { - Ref<VisualScriptFunction> func = E->get().nodes[E->get().function_id].node; + mi.name = p_method; + if (funct.func_id >= 0) { + Ref<VisualScriptFunction> func = nodes[funct.func_id].node; if (func.is_valid()) { for (int i = 0; i < func->get_argument_count(); i++) { PropertyInfo arg; @@ -1028,28 +940,18 @@ void VisualScript::get_script_property_list(List<PropertyInfo> *p_list) const { } int VisualScript::get_member_line(const StringName &p_member) const { -#ifdef TOOLS_ENABLED - if (has_function(p_member)) { - for (Map<int, Function::NodeData>::Element *E = functions[p_member].nodes.front(); E; E = E->next()) { - if (Object::cast_to<VisualScriptFunction>(E->get().node.ptr())) { - return E->key(); - } - } - } -#endif - return -1; + return functions[p_member].func_id; // will be -1 if not found } #ifdef TOOLS_ENABLED bool VisualScript::are_subnodes_edited() const { - for (const Map<StringName, Function>::Element *E = functions.front(); E; E = E->next()) { - for (const Map<int, Function::NodeData>::Element *F = E->get().nodes.front(); F; F = F->next()) { - if (F->get().node->is_edited()) { - return true; - } + List<int> keys; + nodes.get_key_list(&keys); + for (const List<int>::Element *F = keys.front(); F; F = F->next()) { + if (nodes[F->get()].node->is_edited()) { + return true; } } - return false; } #endif @@ -1140,85 +1042,41 @@ void VisualScript::_set_data(const Dictionary &p_data) { Array funcs = d["functions"]; functions.clear(); - Vector2 last_pos = Vector2(-100 * funcs.size(), -100 * funcs.size()); // this is the center of the last fn box - Vector2 last_size = Vector2(0.0, 0.0); - for (int i = 0; i < funcs.size(); i++) { Dictionary func = funcs[i]; - - StringName name = func["name"]; - //int id=func["function_id"]; - add_function(name); - - set_function_scroll(name, func["scroll"]); - - Array nodes = func["nodes"]; - - if (!d.has("vs_unify") && nodes.size() > 0) { - Vector2 top_left = nodes[1]; - Vector2 bottom_right = nodes[1]; - - for (int j = 0; j < nodes.size(); j += 3) { - Point2 pos = nodes[j + 1]; - if (pos.y > top_left.y) { - top_left.y = pos.y; - } - if (pos.y < bottom_right.y) { - bottom_right.y = pos.y; - } - if (pos.x > bottom_right.x) { - bottom_right.x = pos.x; - } - if (pos.x < top_left.x) { - top_left.x = pos.x; - } - } - - Vector2 size = Vector2(bottom_right.x - top_left.x, top_left.y - bottom_right.y); - - Vector2 offset = last_pos + (last_size / 2.0) + (size / 2.0); // dunno I might just keep it in one axis but diagonal feels better.... - - last_pos = offset; - last_size = size; - - for (int j = 0; j < nodes.size(); j += 3) { - add_node(name, nodes[j], nodes[j + 2], offset + nodes[j + 1]); // also add an additional buffer if you want to - } - - } else { - for (int j = 0; j < nodes.size(); j += 3) { - add_node(name, nodes[j], nodes[j + 2], nodes[j + 1]); - } + add_function(func["name"], func["function_id"]); + } + { + Array nodes = d["nodes"]; + for (int i = 0; i < nodes.size(); i += 3) { + add_node(nodes[i], nodes[i + 2], nodes[i + 1]); } - Array sequence_connections = func["sequence_connections"]; + Array sequence_connections = d["sequence_connections"]; for (int j = 0; j < sequence_connections.size(); j += 3) { - sequence_connect(name, sequence_connections[j + 0], sequence_connections[j + 1], sequence_connections[j + 2]); + sequence_connect(sequence_connections[j + 0], sequence_connections[j + 1], sequence_connections[j + 2]); } - Array data_connections = func["data_connections"]; - + Array data_connections = d["data_connections"]; for (int j = 0; j < data_connections.size(); j += 4) { - data_connect(name, data_connections[j + 0], data_connections[j + 1], data_connections[j + 2], data_connections[j + 3]); + data_connect(data_connections[j + 0], data_connections[j + 1], data_connections[j + 2], data_connections[j + 3]); } } + is_tool_script = d["is_tool_script"]; + scroll = d["scroll"]; - if (d.has("is_tool_script")) { - is_tool_script = d["is_tool_script"]; - } else { - is_tool_script = false; - } - - // Takes all the rpc methods + // Takes all the rpc methods. rpc_functions.clear(); rpc_variables.clear(); - for (Map<StringName, Function>::Element *E = functions.front(); E; E = E->next()) { - if (E->get().function_id >= 0 && E->get().nodes.find(E->get().function_id)) { - Ref<VisualScriptFunction> vsf = E->get().nodes[E->get().function_id].node; + List<StringName> fns; + functions.get_key_list(&fns); + for (const List<StringName>::Element *E = fns.front(); E; E = E->next()) { + if (functions[E->get()].func_id >= 0 && nodes.has(functions[E->get()].func_id)) { + Ref<VisualScriptFunction> vsf = nodes[functions[E->get()].func_id].node; if (vsf.is_valid()) { if (vsf->get_rpc_mode() != MultiplayerAPI::RPC_MODE_DISABLED) { ScriptNetData nd; - nd.name = E->key(); + nd.name = E->get(); nd.mode = vsf->get_rpc_mode(); if (rpc_functions.find(nd) == -1) { rpc_functions.push_back(nd); @@ -1228,8 +1086,6 @@ void VisualScript::_set_data(const Dictionary &p_data) { } } - // Visual script doesn't have rset :( - // Sort so we are 100% that they are always the same. rpc_functions.sort_custom<SortNetData>(); } @@ -1237,12 +1093,15 @@ void VisualScript::_set_data(const Dictionary &p_data) { Dictionary VisualScript::_get_data() const { Dictionary d; d["base_type"] = base_type; + Array vars; - for (const Map<StringName, Variable>::Element *E = variables.front(); E; E = E->next()) { - Dictionary var = _get_variable_info(E->key()); - var["name"] = E->key(); //make sure it's the right one - var["default_value"] = E->get().default_value; - var["export"] = E->get()._export; + List<StringName> var_names; + variables.get_key_list(&var_names); + for (const List<StringName>::Element *E = var_names.front(); E; E = E->next()) { + Dictionary var = _get_variable_info(E->get()); + var["name"] = E->get(); // Make sure it's the right one. + var["default_value"] = variables[E->get()].default_value; + var["export"] = variables[E->get()]._export; vars.push_back(var); } d["variables"] = vars; @@ -1264,78 +1123,73 @@ Dictionary VisualScript::_get_data() const { d["signals"] = sigs; Array funcs; - - for (const Map<StringName, Function>::Element *E = functions.front(); E; E = E->next()) { + List<StringName> func_names; + functions.get_key_list(&func_names); + for (const List<StringName>::Element *E = func_names.front(); E; E = E->next()) { Dictionary func; - func["name"] = E->key(); - func["function_id"] = E->get().function_id; - func["scroll"] = E->get().scroll; - - Array nodes; - - for (const Map<int, Function::NodeData>::Element *F = E->get().nodes.front(); F; F = F->next()) { - nodes.push_back(F->key()); - nodes.push_back(F->get().pos); - nodes.push_back(F->get().node); - } - - func["nodes"] = nodes; - - Array sequence_connections; - - for (const Set<SequenceConnection>::Element *F = E->get().sequence_connections.front(); F; F = F->next()) { - sequence_connections.push_back(F->get().from_node); - sequence_connections.push_back(F->get().from_output); - sequence_connections.push_back(F->get().to_node); - } - - func["sequence_connections"] = sequence_connections; - - Array data_connections; + func["name"] = E->get(); + func["function_id"] = functions[E->get()].func_id; + funcs.push_back(func); + } + d["functions"] = funcs; - for (const Set<DataConnection>::Element *F = E->get().data_connections.front(); F; F = F->next()) { - data_connections.push_back(F->get().from_node); - data_connections.push_back(F->get().from_port); - data_connections.push_back(F->get().to_node); - data_connections.push_back(F->get().to_port); - } + Array nds; + List<int> node_ids; + nodes.get_key_list(&node_ids); + for (const List<int>::Element *F = node_ids.front(); F; F = F->next()) { + nds.push_back(F->get()); + nds.push_back(nodes[F->get()].pos); + nds.push_back(nodes[F->get()].node); + } + d["nodes"] = nds; - func["data_connections"] = data_connections; + Array seqconns; + for (const Set<SequenceConnection>::Element *F = sequence_connections.front(); F; F = F->next()) { + seqconns.push_back(F->get().from_node); + seqconns.push_back(F->get().from_output); + seqconns.push_back(F->get().to_node); + } + d["sequence_connections"] = seqconns; - funcs.push_back(func); + Array dataconns; + for (const Set<DataConnection>::Element *F = data_connections.front(); F; F = F->next()) { + dataconns.push_back(F->get().from_node); + dataconns.push_back(F->get().from_port); + dataconns.push_back(F->get().to_node); + dataconns.push_back(F->get().to_port); } + d["data_connections"] = dataconns; - d["functions"] = funcs; d["is_tool_script"] = is_tool_script; - d["vs_unify"] = true; + d["scroll"] = scroll; return d; } void VisualScript::_bind_methods() { - ClassDB::bind_method(D_METHOD("add_function", "name"), &VisualScript::add_function); + ClassDB::bind_method(D_METHOD("add_function", "name", "func_node_id"), &VisualScript::add_function); ClassDB::bind_method(D_METHOD("has_function", "name"), &VisualScript::has_function); ClassDB::bind_method(D_METHOD("remove_function", "name"), &VisualScript::remove_function); ClassDB::bind_method(D_METHOD("rename_function", "name", "new_name"), &VisualScript::rename_function); - ClassDB::bind_method(D_METHOD("set_function_scroll", "name", "ofs"), &VisualScript::set_function_scroll); - ClassDB::bind_method(D_METHOD("get_function_scroll", "name"), &VisualScript::get_function_scroll); + ClassDB::bind_method(D_METHOD("set_scroll", "ofs"), &VisualScript::set_scroll); + ClassDB::bind_method(D_METHOD("get_scroll"), &VisualScript::get_scroll); - ClassDB::bind_method(D_METHOD("add_node", "func", "id", "node", "position"), &VisualScript::add_node, DEFVAL(Point2())); - ClassDB::bind_method(D_METHOD("remove_node", "func", "id"), &VisualScript::remove_node); + ClassDB::bind_method(D_METHOD("add_node", "id", "node", "position"), &VisualScript::add_node, DEFVAL(Point2())); + ClassDB::bind_method(D_METHOD("remove_node", "id"), &VisualScript::remove_node); ClassDB::bind_method(D_METHOD("get_function_node_id", "name"), &VisualScript::get_function_node_id); - ClassDB::bind_method(D_METHOD("get_node", "func", "id"), &VisualScript::get_node); - ClassDB::bind_method(D_METHOD("has_node", "func", "id"), &VisualScript::has_node); - ClassDB::bind_method(D_METHOD("set_node_position", "func", "id", "position"), &VisualScript::set_node_position); - ClassDB::bind_method(D_METHOD("get_node_position", "func", "id"), &VisualScript::get_node_position); + ClassDB::bind_method(D_METHOD("get_node", "id"), &VisualScript::get_node); + ClassDB::bind_method(D_METHOD("has_node", "id"), &VisualScript::has_node); + ClassDB::bind_method(D_METHOD("set_node_position", "id", "position"), &VisualScript::set_node_position); + ClassDB::bind_method(D_METHOD("get_node_position", "id"), &VisualScript::get_node_position); - ClassDB::bind_method(D_METHOD("sequence_connect", "func", "from_node", "from_output", "to_node"), &VisualScript::sequence_connect); - ClassDB::bind_method(D_METHOD("sequence_disconnect", "func", "from_node", "from_output", "to_node"), &VisualScript::sequence_disconnect); - ClassDB::bind_method(D_METHOD("has_sequence_connection", "func", "from_node", "from_output", "to_node"), &VisualScript::has_sequence_connection); + ClassDB::bind_method(D_METHOD("sequence_connect", "from_node", "from_output", "to_node"), &VisualScript::sequence_connect); + ClassDB::bind_method(D_METHOD("sequence_disconnect", "from_node", "from_output", "to_node"), &VisualScript::sequence_disconnect); + ClassDB::bind_method(D_METHOD("has_sequence_connection", "from_node", "from_output", "to_node"), &VisualScript::has_sequence_connection); - ClassDB::bind_method(D_METHOD("data_connect", "func", "from_node", "from_port", "to_node", "to_port"), &VisualScript::data_connect); - ClassDB::bind_method(D_METHOD("data_disconnect", "func", "from_node", "from_port", "to_node", "to_port"), &VisualScript::data_disconnect); - ClassDB::bind_method(D_METHOD("has_data_connection", "func", "from_node", "from_port", "to_node", "to_port"), &VisualScript::has_data_connection); + ClassDB::bind_method(D_METHOD("data_connect", "from_node", "from_port", "to_node", "to_port"), &VisualScript::data_connect); + ClassDB::bind_method(D_METHOD("data_disconnect", "from_node", "from_port", "to_node", "to_port"), &VisualScript::data_disconnect); + ClassDB::bind_method(D_METHOD("has_data_connection", "from_node", "from_port", "to_node", "to_port"), &VisualScript::has_data_connection); ClassDB::bind_method(D_METHOD("add_variable", "name", "default_value", "export"), &VisualScript::add_variable, DEFVAL(Variant()), DEFVAL(false)); ClassDB::bind_method(D_METHOD("has_variable", "name"), &VisualScript::has_variable); @@ -1371,7 +1225,7 @@ void VisualScript::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); - ADD_SIGNAL(MethodInfo("node_ports_changed", PropertyInfo(Variant::STRING, "function"), PropertyInfo(Variant::INT, "id"))); + ADD_SIGNAL(MethodInfo("node_ports_changed", PropertyInfo(Variant::INT, "id"))); } VisualScript::VisualScript() { @@ -1380,16 +1234,12 @@ VisualScript::VisualScript() { } bool VisualScript::inherits_script(const Ref<Script> &p_script) const { - return this == p_script.ptr(); //there is no inheritance in visual scripts, so this is enough -} - -StringName VisualScript::get_default_func() const { - return StringName("f_312843592"); + return this == p_script.ptr(); // There is no inheritance in visual scripts, so this is enough. } -Set<int> VisualScript::get_output_sequence_ports_connected(const String &edited_func, int from_node) { +Set<int> VisualScript::get_output_sequence_ports_connected(int from_node) { List<VisualScript::SequenceConnection> *sc = memnew(List<VisualScript::SequenceConnection>); - get_sequence_connection_list(edited_func, sc); + get_sequence_connection_list(sc); Set<int> connected; for (List<VisualScript::SequenceConnection>::Element *E = sc->front(); E; E = E->next()) { if (E->get().from_node == from_node) { @@ -1401,8 +1251,11 @@ Set<int> VisualScript::get_output_sequence_ports_connected(const String &edited_ } VisualScript::~VisualScript() { - while (!functions.empty()) { - remove_function(functions.front()->key()); + // Remove all nodes and stuff that hold data refs. + List<int> nds; + nodes.get_key_list(&nds); + for (const List<int>::Element *E = nds.front(); E; E = E->next()) { + remove_node(E->get()); } } @@ -1430,20 +1283,21 @@ bool VisualScriptInstance::get(const StringName &p_name, Variant &r_ret) const { } void VisualScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const { - for (const Map<StringName, VisualScript::Variable>::Element *E = script->variables.front(); E; E = E->next()) { - if (!E->get()._export) { + List<StringName> vars; + script->variables.get_key_list(&vars); + for (const List<StringName>::Element *E = vars.front(); E; E = E->next()) { + if (!script->variables[E->get()]._export) { continue; } - PropertyInfo p = E->get().info; - p.name = String(E->key()); + PropertyInfo p = script->variables[E->get()].info; + p.name = String(E->get()); p.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE; p_properties->push_back(p); } } Variant::Type VisualScriptInstance::get_property_type(const StringName &p_name, bool *r_is_valid) const { - const Map<StringName, VisualScript::Variable>::Element *E = script->variables.find(p_name); - if (!E) { + if (!script->variables.has(p_name)) { if (r_is_valid) { *r_is_valid = false; } @@ -1454,19 +1308,17 @@ Variant::Type VisualScriptInstance::get_property_type(const StringName &p_name, *r_is_valid = true; } - return E->get().info.type; + return script->variables[p_name].info.type; } void VisualScriptInstance::get_method_list(List<MethodInfo> *p_list) const { - for (const Map<StringName, VisualScript::Function>::Element *E = script->functions.front(); E; E = E->next()) { - if (E->key() == script->get_default_func()) { - continue; - } - + List<StringName> fns; + script->functions.get_key_list(&fns); + for (const List<StringName>::Element *E = fns.front(); E; E = E->next()) { MethodInfo mi; - mi.name = E->key(); - if (E->get().function_id >= 0 && E->get().nodes.has(E->get().function_id)) { - Ref<VisualScriptFunction> vsf = E->get().nodes[E->get().function_id].node; + mi.name = E->get(); + if (script->functions[E->get()].func_id >= 0 && script->nodes.has(script->functions[E->get()].func_id)) { + Ref<VisualScriptFunction> vsf = script->nodes[script->functions[E->get()].func_id].node; if (vsf.is_valid()) { for (int i = 0; i < vsf->get_argument_count(); i++) { PropertyInfo arg; @@ -1476,21 +1328,16 @@ void VisualScriptInstance::get_method_list(List<MethodInfo> *p_list) const { mi.arguments.push_back(arg); } - if (!vsf->is_sequenced()) { //assumed constant if not sequenced + if (!vsf->is_sequenced()) { // Assumed constant if not sequenced. mi.flags |= METHOD_FLAG_CONST; } } } - p_list->push_back(mi); } } bool VisualScriptInstance::has_method(const StringName &p_method) const { - if (p_method == script->get_default_func()) { - return false; - } - return script->functions.has(p_method); } @@ -1506,7 +1353,7 @@ void VisualScriptInstance::_dependency_step(VisualScriptNodeInstance *node, int pass_stack[node->pass_idx] = p_pass; - if (!node->dependencies.empty()) { + if (!node->dependencies.is_empty()) { int dc = node->dependencies.size(); VisualScriptNodeInstance **deps = node->dependencies.ptrw(); @@ -1522,10 +1369,10 @@ void VisualScriptInstance::_dependency_step(VisualScriptNodeInstance *node, int int index = node->input_ports[i] & VisualScriptNodeInstance::INPUT_MASK; if (node->input_ports[i] & VisualScriptNodeInstance::INPUT_DEFAULT_VALUE_BIT) { - //is a default value (unassigned input port) + // Is a default value (unassigned input port). input_args[i] = &default_values[index]; } else { - //regular temporary in stack + // Rregular temporary in stack. input_args[i] = &variant_stack[index]; } } @@ -1536,7 +1383,7 @@ void VisualScriptInstance::_dependency_step(VisualScriptNodeInstance *node, int Variant *working_mem = node->working_mem_idx >= 0 ? &variant_stack[node->working_mem_idx] : (Variant *)nullptr; node->step(input_args, output_args, VisualScriptNodeInstance::START_MODE_BEGIN_SEQUENCE, working_mem, r_error, error_str); - //ignore return + // Ignore return. if (r_error.error != Callable::CallError::CALL_OK) { *r_error_node = node; } @@ -1547,7 +1394,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p ERR_FAIL_COND_V(!F, Variant()); Function *f = &F->get(); - //this call goes separate, so it can e yielded and suspended + // This call goes separate, so it can e yielded and suspended. Variant *variant_stack = (Variant *)p_stack; bool *sequence_bits = (bool *)(variant_stack + f->max_stack); const Variant **input_args = (const Variant **)(sequence_bits + f->node_count); @@ -1573,27 +1420,27 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p #endif while (true) { - p_pass++; //increment pass + p_pass++; // Increment pass. current_node_id = node->get_id(); VSDEBUG("==========AT NODE: " + itos(current_node_id) + " base: " + node->get_base_node()->get_class_name()); VSDEBUG("AT STACK POS: " + itos(flow_stack_pos)); - //setup working mem + // Setup working mem. working_mem = node->working_mem_idx >= 0 ? &variant_stack[node->working_mem_idx] : (Variant *)nullptr; VSDEBUG("WORKING MEM: " + itos(node->working_mem_idx)); if (current_node_id == f->node) { - //if function node, set up function arguments from beginning of stack + // If function node, set up function arguments from beginning of stack. for (int i = 0; i < f->argument_count; i++) { input_args[i] = &variant_stack[i]; } } else { - //run dependencies first + // Run dependencies first. - if (!node->dependencies.empty()) { + if (!node->dependencies.is_empty()) { int dc = node->dependencies.size(); VisualScriptNodeInstance **deps = node->dependencies.ptrw(); @@ -1608,18 +1455,18 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p } if (!error) { - //setup input pointers normally + // Setup input pointers normally. VSDEBUG("INPUT PORTS: " + itos(node->input_port_count)); for (int i = 0; i < node->input_port_count; i++) { int index = node->input_ports[i] & VisualScriptNodeInstance::INPUT_MASK; if (node->input_ports[i] & VisualScriptNodeInstance::INPUT_DEFAULT_VALUE_BIT) { - //is a default value (unassigned input port) + // Is a default value (unassigned input port). input_args[i] = &default_values[index]; VSDEBUG("\tPORT " + itos(i) + " DEFAULT VAL"); } else { - //regular temporary in stack + // Regular temporary in stack. input_args[i] = &variant_stack[index]; VSDEBUG("PORT " + itos(i) + " AT STACK " + itos(index)); } @@ -1631,7 +1478,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p break; } - //setup output pointers + // Setup output pointers. VSDEBUG("OUTPUT PORTS: " + itos(node->output_port_count)); for (int i = 0; i < node->output_port_count; i++) { @@ -1639,15 +1486,15 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p VSDEBUG("PORT " + itos(i) + " AT STACK " + itos(node->output_ports[i])); } - //do step + // Do step. VisualScriptNodeInstance::StartMode start_mode; { if (p_resuming_yield) { start_mode = VisualScriptNodeInstance::START_MODE_RESUME_YIELD; - p_resuming_yield = false; // should resume only the first time + p_resuming_yield = false; // Should resume only the first time. } else if (flow_stack && (flow_stack[flow_stack_pos] & VisualScriptNodeInstance::FLOW_STACK_PUSHED_BIT)) { - //if there is a push bit, it means we are continuing a sequence + // If there is a push bit, it means we are continuing a sequence. start_mode = VisualScriptNodeInstance::START_MODE_CONTINUE_SEQUENCE; } else { start_mode = VisualScriptNodeInstance::START_MODE_BEGIN_SEQUENCE; @@ -1659,13 +1506,13 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p int ret = node->step(input_args, output_args, start_mode, working_mem, r_error, error_str); if (r_error.error != Callable::CallError::CALL_OK) { - //use error from step + // Use error from step. error = true; break; } if (ret & VisualScriptNodeInstance::STEP_YIELD_BIT) { - //yielded! + // Yielded! if (node->get_working_memory_size() == 0) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; error_str = RTR("A node yielded without working memory, please read the docs on how to yield properly!"); @@ -1681,7 +1528,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p break; } - //step 1, capture all state + // Step 1, capture all state. state->instance_id = get_owner_ptr()->get_instance_id(); state->script_id = get_script()->get_instance_id(); state->instance = this; @@ -1693,11 +1540,11 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p state->stack.resize(p_stack_size); state->pass = p_pass; copymem(state->stack.ptrw(), p_stack, p_stack_size); - //step 2, run away, return directly + // Step 2, run away, return directly. r_error.error = Callable::CallError::CALL_OK; #ifdef DEBUG_ENABLED - //will re-enter later, so exiting + // Will re-enter later, so exiting. if (EngineDebugger::is_active()) { VisualScriptLanguage::singleton->exit_function(); } @@ -1742,18 +1589,18 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p error_str = RTR("Return value must be assigned to first element of node working memory! Fix your node please."); error = true; } else { - //assign from working memory, first element + // Assign from working memory, first element. return_value = *working_mem; } VSDEBUG("EXITING FUNCTION - VALUE " + String(return_value)); - break; //exit function requested, bye + break; // Exit function requested, bye } - VisualScriptNodeInstance *next = nullptr; //next node + VisualScriptNodeInstance *next = nullptr; // Next node. if ((ret == output || ret & VisualScriptNodeInstance::STEP_FLAG_PUSH_STACK_BIT) && node->sequence_output_count) { - //if no exit bit was set, and has sequence outputs, guess next node + // If no exit bit was set, and has sequence outputs, guess next node. if (output >= node->sequence_output_count) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; error_str = RTR("Node returned an invalid sequence output: ") + itos(output); @@ -1766,21 +1613,21 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p } if (flow_stack) { - //update flow stack pos (may have changed) + // Update flow stack pos (may have changed). flow_stack[flow_stack_pos] = current_node_id; - //add stack push bit if requested + // Add stack push bit if requested. if (ret & VisualScriptNodeInstance::STEP_FLAG_PUSH_STACK_BIT) { flow_stack[flow_stack_pos] |= VisualScriptNodeInstance::FLOW_STACK_PUSHED_BIT; - sequence_bits[node->sequence_index] = true; //remember sequence bit + sequence_bits[node->sequence_index] = true; // Remember sequence bit. VSDEBUG("NEXT SEQ - FLAG BIT"); } else { - sequence_bits[node->sequence_index] = false; //forget sequence bit + sequence_bits[node->sequence_index] = false; // Forget sequence bit. VSDEBUG("NEXT SEQ - NORMAL"); } if (ret & VisualScriptNodeInstance::STEP_FLAG_GO_BACK_BIT) { - //go back request + // Go back request. if (flow_stack_pos > 0) { flow_stack_pos--; @@ -1788,20 +1635,20 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p VSDEBUG("NEXT IS GO BACK"); } else { VSDEBUG("NEXT IS GO BACK, BUT NO NEXT SO EXIT"); - break; //simply exit without value or error + break; // Simply exit without value or error. } } else if (next) { if (sequence_bits[next->sequence_index]) { - // what happened here is that we are entering a node that is in the middle of doing a sequence (pushed stack) from the front + // What happened here is that we are entering a node that is in the middle of doing a sequence (pushed stack) from the front // because each node has a working memory, we can't really do a sub-sequence // as a result, the sequence will be restarted and the stack will roll back to find where this node - // started the sequence + // started the sequence. bool found = false; for (int i = flow_stack_pos; i >= 0; i--) { if ((flow_stack[i] & VisualScriptNodeInstance::FLOW_STACK_MASK) == next->get_id()) { - flow_stack_pos = i; //roll back and remove bit + flow_stack_pos = i; // Roll back and remove bit. flow_stack[i] = next->get_id(); sequence_bits[next->sequence_index] = false; found = true; @@ -1819,7 +1666,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p VSDEBUG("RE-ENTERED A LOOP, RETURNED STACK POS TO - " + itos(flow_stack_pos)); } else { - // check for stack overflow + // Check for stack overflow. if (flow_stack_pos + 1 >= flow_max) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; error_str = RTR("Stack overflow with stack depth: ") + itos(output); @@ -1836,7 +1683,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p } } else { - //no next node, try to go back in stack to pushed bit + // No next node, try to go back in stack to pushed bit. bool found = false; @@ -1852,22 +1699,22 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p if (!found) { VSDEBUG("NO NEXT NODE, NO GO BACK, EXITING"); - break; //done, couldn't find a push stack bit + break; // Done, couldn't find a push stack bit. } VSDEBUG("NO NEXT NODE, GO BACK TO: " + itos(flow_stack_pos)); } } else { - node = next; //stackless mode, simply assign next node + node = next; // Stackless mode, simply assign next node. } } if (error) { - //error - // function, file, line, error, explanation + // Error + // Function, file, line, error, explanation. String err_file = script->get_path(); String err_func = p_method; - int err_line = current_node_id; //not a line but it works as one + int err_line = current_node_id; // Not a line but it works as one. if (node && (r_error.error != Callable::CallError::CALL_ERROR_INVALID_METHOD || error_str == String())) { if (error_str != String()) { @@ -1906,7 +1753,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p } #endif - //clean up variant stack + // Clean up variant stack. for (int i = 0; i < f->max_stack; i++) { variant_stack[i].~Variant(); } @@ -1954,7 +1801,7 @@ Variant VisualScriptInstance::call(const StringName &p_method, const Variant **p int *pass_stack = flow_stack ? (int *)(flow_stack + flow_max) : (int *)nullptr; for (int i = 0; i < f->node_count; i++) { - sequence_bits[i] = false; //all starts as false + sequence_bits[i] = false; // All starts as false. } zeromem(pass_stack, f->pass_stack_size * sizeof(int)); @@ -1988,12 +1835,12 @@ Variant VisualScriptInstance::call(const StringName &p_method, const Variant **p return Variant(); } - //allocate variant stack + // Allocate variant stack. for (int i = 0; i < f->max_stack; i++) { memnew_placement(&variant_stack[i], Variant); } - //allocate function arguments (must be copied for yield to work properly) + // Allocate function arguments (must be copied for yield to work properly). for (int i = 0; i < p_argcount; i++) { variant_stack[i] = *p_args[i]; } @@ -2002,12 +1849,12 @@ Variant VisualScriptInstance::call(const StringName &p_method, const Variant **p } void VisualScriptInstance::notification(int p_notification) { - //do nothing as this is called using virtual + // Do nothing as this is called using virtual. Variant what = p_notification; const Variant *whatp = &what; Callable::CallError ce; - call(VisualScriptLanguage::singleton->notification, &whatp, 1, ce); //do as call + call(VisualScriptLanguage::singleton->notification, &whatp, 1, ce); // Do as call. } String VisualScriptInstance::to_string(bool *r_valid) { @@ -2086,7 +1933,7 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o max_output_args = 0; if (Object::cast_to<Node>(p_owner)) { - //turn on these if they exist and base is a node + // Turn on these if they exist and base is a node. Node *node = Object::cast_to<Node>(p_owner); if (p_script->functions.has("_process")) { node->set_process(true); @@ -2105,190 +1952,236 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o } } - for (const Map<StringName, VisualScript::Variable>::Element *E = script->variables.front(); E; E = E->next()) { - variables[E->key()] = E->get().default_value; - } - - for (const Map<StringName, VisualScript::Function>::Element *E = script->functions.front(); E; E = E->next()) { - if (E->key() == script->get_default_func()) { - continue; - } - - Function function; - function.node = E->get().function_id; - function.max_stack = 0; - function.flow_stack_size = 0; - function.pass_stack_size = 0; - function.node_count = 0; - - Map<StringName, int> local_var_indices; - - if (function.node < 0) { - VisualScriptLanguage::singleton->debug_break_parse(get_script()->get_path(), 0, "No start node in function: " + String(E->key())); - - ERR_CONTINUE(function.node < 0); + // Setup variables. + { + List<StringName> keys; + script->variables.get_key_list(&keys); + for (const List<StringName>::Element *E = keys.front(); E; E = E->next()) { + variables[E->get()] = script->variables[E->get()].default_value; } + } - { - Ref<VisualScriptFunction> func_node = script->get_node(E->key(), E->get().function_id); - - if (func_node.is_null()) { - VisualScriptLanguage::singleton->debug_break_parse(get_script()->get_path(), 0, "No VisualScriptFunction typed start node in function: " + String(E->key())); + // Setup functions from sequence trees. + { + List<StringName> keys; + script->functions.get_key_list(&keys); + for (const List<StringName>::Element *E = keys.front(); E; E = E->next()) { + const VisualScript::Function vsfn = p_script->functions[E->get()]; + Function function; + function.node = vsfn.func_id; + function.max_stack = 0; + function.flow_stack_size = 0; + function.pass_stack_size = 0; + function.node_count = 0; + + Map<StringName, int> local_var_indices; + + if (function.node < 0) { + VisualScriptLanguage::singleton->debug_break_parse(get_script()->get_path(), 0, "No start node in function: " + String(E->get())); + ERR_CONTINUE(function.node < 0); } - ERR_CONTINUE(!func_node.is_valid()); - - function.argument_count = func_node->get_argument_count(); - function.max_stack += function.argument_count; - function.flow_stack_size = func_node->is_stack_less() ? 0 : func_node->get_stack_size(); - max_input_args = MAX(max_input_args, function.argument_count); - } - - //multiple passes are required to set up this complex thing.. - - //first create the nodes - for (const Map<int, VisualScript::Function::NodeData>::Element *F = E->get().nodes.front(); F; F = F->next()) { - Ref<VisualScriptNode> node = F->get().node; - - VisualScriptNodeInstance *instance = node->instance(this); //create instance - ERR_FAIL_COND(!instance); + { + Ref<VisualScriptFunction> func_node = script->get_node(vsfn.func_id); - instance->base = node.ptr(); + if (func_node.is_null()) { + VisualScriptLanguage::singleton->debug_break_parse(get_script()->get_path(), 0, "No VisualScriptFunction typed start node in function: " + String(E->get())); + } - instance->id = F->key(); - instance->input_port_count = node->get_input_value_port_count(); - instance->input_ports = nullptr; - instance->output_port_count = node->get_output_value_port_count(); - instance->output_ports = nullptr; - instance->sequence_output_count = node->get_output_sequence_port_count(); - instance->sequence_index = function.node_count++; - instance->sequence_outputs = nullptr; - instance->pass_idx = -1; + ERR_CONTINUE(!func_node.is_valid()); - if (instance->input_port_count) { - instance->input_ports = memnew_arr(int, instance->input_port_count); - for (int i = 0; i < instance->input_port_count; i++) { - instance->input_ports[i] = -1; //if not assigned, will become default value - } + function.argument_count = func_node->get_argument_count(); + function.max_stack += function.argument_count; + function.flow_stack_size = func_node->is_stack_less() ? 0 : func_node->get_stack_size(); + max_input_args = MAX(max_input_args, function.argument_count); } - - if (instance->output_port_count) { - instance->output_ports = memnew_arr(int, instance->output_port_count); - for (int i = 0; i < instance->output_port_count; i++) { - instance->output_ports[i] = -1; //if not assigned, will output to trash + // Function nodes graphs. + Set<VisualScript::SequenceConnection> seqconns; + Set<VisualScript::DataConnection> dataconns; + Set<int> node_ids; + node_ids.insert(function.node); + { + List<int> nd_queue; + nd_queue.push_back(function.node); + while (!nd_queue.is_empty()) { + for (const Set<VisualScript::SequenceConnection>::Element *F = script->sequence_connections.front(); F; F = F->next()) { + if (nd_queue.front()->get() == F->get().from_node && !node_ids.has(F->get().to_node)) { + nd_queue.push_back(F->get().to_node); + node_ids.insert(F->get().to_node); + } + if (nd_queue.front()->get() == F->get().from_node && !seqconns.has(F->get())) { + seqconns.insert(F->get()); + } + } + nd_queue.pop_front(); } - } - - if (instance->sequence_output_count) { - instance->sequence_outputs = memnew_arr(VisualScriptNodeInstance *, instance->sequence_output_count); - for (int i = 0; i < instance->sequence_output_count; i++) { - instance->sequence_outputs[i] = nullptr; //if it remains null, flow ends here + HashMap<int, HashMap<int, Pair<int, int>>> dc_lut; // :: to -> to_port -> (from, from_port) + for (const Set<VisualScript::DataConnection>::Element *F = script->data_connections.front(); F; F = F->next()) { + dc_lut[F->get().to_node][F->get().to_port] = Pair<int, int>(F->get().from_node, F->get().from_port); + } + for (const Set<int>::Element *F = node_ids.front(); F; F = F->next()) { + nd_queue.push_back(F->get()); + } + List<int> dc_keys; + while (!nd_queue.is_empty()) { + int ky = nd_queue.front()->get(); + dc_lut[ky].get_key_list(&dc_keys); + for (const List<int>::Element *F = dc_keys.front(); F; F = F->next()) { + VisualScript::DataConnection dc; + dc.from_node = dc_lut[ky][F->get()].first; + dc.from_port = dc_lut[ky][F->get()].second; + dc.to_node = ky; + dc.to_port = F->get(); + dataconns.insert(dc); + nd_queue.push_back(dc.from_node); + node_ids.insert(dc.from_node); + } + dc_keys.clear(); // Necessary as get_key_list does a push_back not a set. + nd_queue.pop_front(); } } - if (Object::cast_to<VisualScriptLocalVar>(node.ptr()) || Object::cast_to<VisualScriptLocalVarSet>(*node)) { - //working memory is shared only for this node, for the same variables - Ref<VisualScriptLocalVar> vslv = node; - - StringName var_name; + //Multiple passes are required to set up this complex thing.. + //First create the nodes. + for (const Set<int>::Element *F = node_ids.front(); F; F = F->next()) { + Ref<VisualScriptNode> node = script->nodes[F->get()].node; + + VisualScriptNodeInstance *instance = node->instance(this); // Create instance. + ERR_FAIL_COND(!instance); + + instance->base = node.ptr(); + + instance->id = F->get(); + instance->input_port_count = node->get_input_value_port_count(); + instance->input_ports = NULL; + instance->output_port_count = node->get_output_value_port_count(); + instance->output_ports = NULL; + instance->sequence_output_count = node->get_output_sequence_port_count(); + instance->sequence_index = function.node_count++; + instance->sequence_outputs = NULL; + instance->pass_idx = -1; + + if (instance->input_port_count) { + instance->input_ports = memnew_arr(int, instance->input_port_count); + for (int i = 0; i < instance->input_port_count; i++) { + instance->input_ports[i] = -1; // If not assigned, will become default value. + } + } - if (Object::cast_to<VisualScriptLocalVar>(*node)) { - var_name = String(Object::cast_to<VisualScriptLocalVar>(*node)->get_var_name()).strip_edges(); - } else { - var_name = String(Object::cast_to<VisualScriptLocalVarSet>(*node)->get_var_name()).strip_edges(); + if (instance->output_port_count) { + instance->output_ports = memnew_arr(int, instance->output_port_count); + for (int i = 0; i < instance->output_port_count; i++) { + instance->output_ports[i] = -1; // If not assigned, will output to trash. + } } - if (!local_var_indices.has(var_name)) { - local_var_indices[var_name] = function.max_stack; - function.max_stack++; + if (instance->sequence_output_count) { + instance->sequence_outputs = memnew_arr(VisualScriptNodeInstance *, instance->sequence_output_count); + for (int i = 0; i < instance->sequence_output_count; i++) { + instance->sequence_outputs[i] = NULL; // If it remains null, flow ends here. + } } - instance->working_mem_idx = local_var_indices[var_name]; + if (Object::cast_to<VisualScriptLocalVar>(node.ptr()) || Object::cast_to<VisualScriptLocalVarSet>(*node)) { + // Working memory is shared only for this node, for the same variables. + Ref<VisualScriptLocalVar> vslv = node; - } else if (instance->get_working_memory_size()) { - instance->working_mem_idx = function.max_stack; - function.max_stack += instance->get_working_memory_size(); - } else { - instance->working_mem_idx = -1; //no working mem - } + StringName var_name; - max_input_args = MAX(max_input_args, instance->input_port_count); - max_output_args = MAX(max_output_args, instance->output_port_count); + if (Object::cast_to<VisualScriptLocalVar>(*node)) + var_name = String(Object::cast_to<VisualScriptLocalVar>(*node)->get_var_name()).strip_edges(); + else + var_name = String(Object::cast_to<VisualScriptLocalVarSet>(*node)->get_var_name()).strip_edges(); - instances[F->key()] = instance; - } + if (!local_var_indices.has(var_name)) { + local_var_indices[var_name] = function.max_stack; + function.max_stack++; + } - function.trash_pos = function.max_stack++; //create pos for trash + instance->working_mem_idx = local_var_indices[var_name]; - //second pass, do data connections + } else if (instance->get_working_memory_size()) { + instance->working_mem_idx = function.max_stack; + function.max_stack += instance->get_working_memory_size(); + } else { + instance->working_mem_idx = -1; //no working mem + } - for (const Set<VisualScript::DataConnection>::Element *F = E->get().data_connections.front(); F; F = F->next()) { - VisualScript::DataConnection dc = F->get(); - ERR_CONTINUE(!instances.has(dc.from_node)); - VisualScriptNodeInstance *from = instances[dc.from_node]; - ERR_CONTINUE(!instances.has(dc.to_node)); - VisualScriptNodeInstance *to = instances[dc.to_node]; - ERR_CONTINUE(dc.from_port >= from->output_port_count); - ERR_CONTINUE(dc.to_port >= to->input_port_count); + max_input_args = MAX(max_input_args, instance->input_port_count); + max_output_args = MAX(max_output_args, instance->output_port_count); - if (from->output_ports[dc.from_port] == -1) { - int stack_pos = function.max_stack++; - from->output_ports[dc.from_port] = stack_pos; + instances[F->get()] = instance; } - if (from->get_sequence_output_count() == 0 && to->dependencies.find(from) == -1) { - //if the node we are reading from has no output sequence, we must call step() before reading from it. - if (from->pass_idx == -1) { - from->pass_idx = function.pass_stack_size; - function.pass_stack_size++; + function.trash_pos = function.max_stack++; // create pos for trash + + // Second pass, do data connections. + for (const Set<VisualScript::DataConnection>::Element *F = dataconns.front(); F; F = F->next()) { + VisualScript::DataConnection dc = F->get(); + ERR_CONTINUE(!instances.has(dc.from_node)); + VisualScriptNodeInstance *from = instances[dc.from_node]; + ERR_CONTINUE(!instances.has(dc.to_node)); + VisualScriptNodeInstance *to = instances[dc.to_node]; + ERR_CONTINUE(dc.from_port >= from->output_port_count); + ERR_CONTINUE(dc.to_port >= to->input_port_count); + + if (from->output_ports[dc.from_port] == -1) { + int stack_pos = function.max_stack++; + from->output_ports[dc.from_port] = stack_pos; } - to->dependencies.push_back(from); - } - to->input_ports[dc.to_port] = from->output_ports[dc.from_port]; //read from wherever the stack is - } - - //third pass, do sequence connections + if (from->get_sequence_output_count() == 0 && to->dependencies.find(from) == -1) { + // If the node we are reading from has no output sequence, we must call step() before reading from it. + if (from->pass_idx == -1) { + from->pass_idx = function.pass_stack_size; + function.pass_stack_size++; + } + to->dependencies.push_back(from); + } - for (const Set<VisualScript::SequenceConnection>::Element *F = E->get().sequence_connections.front(); F; F = F->next()) { - VisualScript::SequenceConnection sc = F->get(); - ERR_CONTINUE(!instances.has(sc.from_node)); - VisualScriptNodeInstance *from = instances[sc.from_node]; - ERR_CONTINUE(!instances.has(sc.to_node)); - VisualScriptNodeInstance *to = instances[sc.to_node]; - ERR_CONTINUE(sc.from_output >= from->sequence_output_count); + to->input_ports[dc.to_port] = from->output_ports[dc.from_port]; // Read from wherever the stack is. + } - from->sequence_outputs[sc.from_output] = to; - } + // Third pass, do sequence connections. + for (const Set<VisualScript::SequenceConnection>::Element *F = seqconns.front(); F; F = F->next()) { + VisualScript::SequenceConnection sc = F->get(); + ERR_CONTINUE(!instances.has(sc.from_node)); + VisualScriptNodeInstance *from = instances[sc.from_node]; + ERR_CONTINUE(!instances.has(sc.to_node)); + VisualScriptNodeInstance *to = instances[sc.to_node]; + ERR_CONTINUE(sc.from_output >= from->sequence_output_count); - //fourth pass: - // 1) unassigned input ports to default values - // 2) connect unassigned output ports to trash + from->sequence_outputs[sc.from_output] = to; + } - for (const Map<int, VisualScript::Function::NodeData>::Element *F = E->get().nodes.front(); F; F = F->next()) { - ERR_CONTINUE(!instances.has(F->key())); + //fourth pass: + // 1) unassigned input ports to default values + // 2) connect unassigned output ports to trash + for (const Set<int>::Element *F = node_ids.front(); F; F = F->next()) { + ERR_CONTINUE(!instances.has(F->get())); - Ref<VisualScriptNode> node = F->get().node; - VisualScriptNodeInstance *instance = instances[F->key()]; + Ref<VisualScriptNode> node = script->nodes[F->get()].node; + VisualScriptNodeInstance *instance = instances[F->get()]; - // connect to default values - for (int i = 0; i < instance->input_port_count; i++) { - if (instance->input_ports[i] == -1) { - //unassigned, connect to default val - instance->input_ports[i] = default_values.size() | VisualScriptNodeInstance::INPUT_DEFAULT_VALUE_BIT; - default_values.push_back(node->get_default_input_value(i)); + // Connect to default values. + for (int i = 0; i < instance->input_port_count; i++) { + if (instance->input_ports[i] == -1) { + // Unassigned, connect to default val. + instance->input_ports[i] = default_values.size() | VisualScriptNodeInstance::INPUT_DEFAULT_VALUE_BIT; + default_values.push_back(node->get_default_input_value(i)); + } } - } - // connect to trash - for (int i = 0; i < instance->output_port_count; i++) { - if (instance->output_ports[i] == -1) { - instance->output_ports[i] = function.trash_pos; //trash is same for all + // Connect to trash. + for (int i = 0; i < instance->output_port_count; i++) { + if (instance->output_ports[i] == -1) { + instance->output_ports[i] = function.trash_pos; //trash is same for all + } } } - } - functions[E->key()] = function; + functions[E->get()] = function; + } } } @@ -2354,7 +2247,7 @@ Variant VisualScriptFunctionState::_signal_callback(const Variant **p_args, int Variant *working_mem = ((Variant *)stack.ptr()) + working_mem_index; - *working_mem = args; //arguments go to working mem. + *working_mem = args; // Arguments go to working mem. Variant ret = instance->_call_internal(function, stack.ptrw(), stack.size(), node, flow_stack_pos, pass, true, r_error); function = StringName(); //invalidate @@ -2367,7 +2260,7 @@ void VisualScriptFunctionState::connect_to_signal(Object *p_obj, const String &p binds.push_back(p_binds[i]); } binds.push_back(Ref<VisualScriptFunctionState>(this)); //add myself on the back to avoid dying from unreferencing - p_obj->connect_compat(p_signal, this, "_signal_callback", binds, CONNECT_ONESHOT); + p_obj->connect(p_signal, Callable(this, "_signal_callback"), binds, CONNECT_ONESHOT); } bool VisualScriptFunctionState::is_valid() const { @@ -2388,7 +2281,7 @@ Variant VisualScriptFunctionState::resume(Array p_args) { Variant *working_mem = ((Variant *)stack.ptr()) + working_mem_index; - *working_mem = p_args; //arguments go to working mem. + *working_mem = p_args; // Arguments go to working mem. Variant ret = instance->_call_internal(function, stack.ptrw(), stack.size(), node, flow_stack_pos, pass, true, r_error); function = StringName(); //invalidate @@ -2498,7 +2391,7 @@ void VisualScriptLanguage::add_global_constant(const StringName &p_variable, con /* DEBUGGER FUNCTIONS */ bool VisualScriptLanguage::debug_break_parse(const String &p_file, int p_node, const String &p_error) { - //break because of parse error + // Break because of parse error. if (EngineDebugger::is_active() && Thread::get_caller_id() == Thread::get_main_id()) { _debug_parse_err_node = p_node; @@ -2666,7 +2559,7 @@ void VisualScriptLanguage::debug_get_stack_level_members(int p_level, List<Strin } void VisualScriptLanguage::debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) { - //no globals are really reachable in gdscript + // No globals are really reachable in gdscript. } String VisualScriptLanguage::debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems, int p_max_depth) { @@ -2742,7 +2635,7 @@ VisualScriptLanguage::VisualScriptLanguage() { ProjectSettings::get_singleton()->set_custom_property_info("debug/settings/visual_script/max_call_stack", PropertyInfo(Variant::INT, "debug/settings/visual_script/max_call_stack", PROPERTY_HINT_RANGE, "1024,4096,1,or_greater")); //minimum is 1024 if (EngineDebugger::is_active()) { - //debugging enabled! + // Debugging enabled! _debug_max_call_stack = dmcs; _call_stack = memnew_arr(CallLevel, _debug_max_call_stack + 1); diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h index 85dab4e6cf..bdb3c3a16b 100644 --- a/modules/visual_script/visual_script.h +++ b/modules/visual_script/visual_script.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,6 +33,7 @@ #include "core/debugger/engine_debugger.h" #include "core/debugger/script_debugger.h" +#include "core/doc_data.h" #include "core/object/script_language.h" #include "core/os/thread.h" @@ -45,7 +46,7 @@ class VisualScriptNode : public Resource { friend class VisualScript; - Set<VisualScript *> scripts_used; + Ref<VisualScript> script_used; Array default_input_values; bool breakpoint; @@ -82,7 +83,7 @@ public: virtual String get_text() const; virtual String get_category() const = 0; - //used by editor, this is not really saved + // Used by editor, this is not really saved. void set_breakpoint(bool p_breakpoint); bool is_breakpoint() const; @@ -105,9 +106,9 @@ public: class VisualScriptNodeInstance { friend class VisualScriptInstance; - friend class VisualScriptLanguage; //for debugger + friend class VisualScriptLanguage; // For debugger. - enum { //input argument addressing + enum { // Input argument addressing. INPUT_SHIFT = 1 << 24, INPUT_MASK = INPUT_SHIFT - 1, INPUT_DEFAULT_VALUE_BIT = INPUT_SHIFT, // from unassigned input port, using default value (edited by user) @@ -137,13 +138,13 @@ public: enum { STEP_SHIFT = 1 << 24, STEP_MASK = STEP_SHIFT - 1, - STEP_FLAG_PUSH_STACK_BIT = STEP_SHIFT, //push bit to stack - STEP_FLAG_GO_BACK_BIT = STEP_SHIFT << 1, //go back to previous node - STEP_NO_ADVANCE_BIT = STEP_SHIFT << 2, //do not advance past this node - STEP_EXIT_FUNCTION_BIT = STEP_SHIFT << 3, //return from function - STEP_YIELD_BIT = STEP_SHIFT << 4, //yield (will find VisualScriptFunctionState state in first working memory) + STEP_FLAG_PUSH_STACK_BIT = STEP_SHIFT, // push bit to stack + STEP_FLAG_GO_BACK_BIT = STEP_SHIFT << 1, // go back to previous node + STEP_NO_ADVANCE_BIT = STEP_SHIFT << 2, // do not advance past this node + STEP_EXIT_FUNCTION_BIT = STEP_SHIFT << 3, // return from function + STEP_YIELD_BIT = STEP_SHIFT << 4, // yield (will find VisualScriptFunctionState state in first working memory) - FLOW_STACK_PUSHED_BIT = 1 << 30, //in flow stack, means bit was pushed (must go back here if end of sequence) + FLOW_STACK_PUSHED_BIT = 1 << 30, // in flow stack, means bit was pushed (must go back here if end of sequence) FLOW_STACK_MASK = FLOW_STACK_PUSHED_BIT - 1 }; @@ -156,7 +157,7 @@ public: virtual int get_working_memory_size() const { return 0; } - virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) = 0; //do a step, return which sequence port to go out + virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) = 0; // Do a step, return which sequence port to go out. Ref<VisualScriptNode> get_base_node() { return Ref<VisualScriptNode>(base); } @@ -210,34 +211,32 @@ private: Variant::Type type; }; - struct Function { - struct NodeData { - Point2 pos; - Ref<VisualScriptNode> node; - }; - - Map<int, NodeData> nodes; - - Set<SequenceConnection> sequence_connections; + struct NodeData { + Point2 pos; + Ref<VisualScriptNode> node; + }; - Set<DataConnection> data_connections; + HashMap<int, NodeData> nodes; // Can be a sparse map. - int function_id; + Set<SequenceConnection> sequence_connections; + Set<DataConnection> data_connections; - Vector2 scroll; + Vector2 scroll; - Function() { function_id = -1; } + struct Function { + int func_id; + Function() { func_id = -1; } }; struct Variable { PropertyInfo info; Variant default_value; bool _export; - // add getter & setter options here + // Add getter & setter options here. }; - Map<StringName, Function> functions; - Map<StringName, Variable> variables; + HashMap<StringName, Function> functions; + HashMap<StringName, Variable> variables; Map<StringName, Vector<Argument>> custom_signals; Vector<ScriptNetData> rpc_functions; Vector<ScriptNetData> rpc_variables; @@ -248,7 +247,7 @@ private: #ifdef TOOLS_ENABLED Set<PlaceHolderScriptInstance *> placeholders; - //void _update_placeholder(PlaceHolderScriptInstance *p_placeholder); + // void _update_placeholder(PlaceHolderScriptInstance *p_placeholder); virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder) override; void _update_placeholders(); #endif @@ -266,37 +265,38 @@ protected: public: bool inherits_script(const Ref<Script> &p_script) const override; - // TODO: Remove it in future when breaking changes are acceptable - StringName get_default_func() const; - void add_function(const StringName &p_name); + void set_scroll(const Vector2 &p_scroll); + Vector2 get_scroll() const; + + void add_function(const StringName &p_name, int p_func_node_id); bool has_function(const StringName &p_name) const; void remove_function(const StringName &p_name); void rename_function(const StringName &p_name, const StringName &p_new_name); - void set_function_scroll(const StringName &p_name, const Vector2 &p_scroll); - Vector2 get_function_scroll(const StringName &p_name) const; void get_function_list(List<StringName> *r_functions) const; int get_function_node_id(const StringName &p_name) const; void set_tool_enabled(bool p_enabled); - void add_node(const StringName &p_func, int p_id, const Ref<VisualScriptNode> &p_node, const Point2 &p_pos = Point2()); - void remove_node(const StringName &p_func, int p_id); - bool has_node(const StringName &p_func, int p_id) const; - Ref<VisualScriptNode> get_node(const StringName &p_func, int p_id) const; - void set_node_position(const StringName &p_func, int p_id, const Point2 &p_pos); - Point2 get_node_position(const StringName &p_func, int p_id) const; - void get_node_list(const StringName &p_func, List<int> *r_nodes) const; - - void sequence_connect(const StringName &p_func, int p_from_node, int p_from_output, int p_to_node); - void sequence_disconnect(const StringName &p_func, int p_from_node, int p_from_output, int p_to_node); - bool has_sequence_connection(const StringName &p_func, int p_from_node, int p_from_output, int p_to_node) const; - void get_sequence_connection_list(const StringName &p_func, List<SequenceConnection> *r_connection) const; - - void data_connect(const StringName &p_func, int p_from_node, int p_from_port, int p_to_node, int p_to_port); - void data_disconnect(const StringName &p_func, int p_from_node, int p_from_port, int p_to_node, int p_to_port); - bool has_data_connection(const StringName &p_func, int p_from_node, int p_from_port, int p_to_node, int p_to_port) const; - void get_data_connection_list(const StringName &p_func, List<DataConnection> *r_connection) const; - bool is_input_value_port_connected(const StringName &p_func, int p_node, int p_port) const; - bool get_input_value_port_connection_source(const StringName &p_func, int p_node, int p_port, int *r_node, int *r_port) const; + void add_node(int p_id, const Ref<VisualScriptNode> &p_node, const Point2 &p_pos = Point2()); + void remove_node(int p_id); + bool has_node(int p_id) const; + Ref<VisualScriptNode> get_node(int p_id) const; + void set_node_position(int p_id, const Point2 &p_pos); + Point2 get_node_position(int p_id) const; + void get_node_list(List<int> *r_nodes) const; + + void sequence_connect(int p_from_node, int p_from_output, int p_to_node); + void sequence_disconnect(int p_from_node, int p_from_output, int p_to_node); + bool has_sequence_connection(int p_from_node, int p_from_output, int p_to_node) const; + void get_sequence_connection_list(List<SequenceConnection> *r_connection) const; + Set<int> get_output_sequence_ports_connected(int from_node); + + void data_connect(int p_from_node, int p_from_port, int p_to_node, int p_to_port); + void data_disconnect(int p_from_node, int p_from_port, int p_to_node, int p_to_port); + bool has_data_connection(int p_from_node, int p_from_port, int p_to_node, int p_to_port) const; + void get_data_connection_list(List<DataConnection> *r_connection) const; + + bool is_input_value_port_connected(int p_node, int p_port) const; + bool get_input_value_port_connection_source(int p_node, int p_port, int *r_node, int *r_port) const; void add_variable(const StringName &p_name, const Variant &p_default_value = Variant(), bool p_export = false); bool has_variable(const StringName &p_name) const; @@ -342,6 +342,13 @@ public: virtual void set_source_code(const String &p_code) override; virtual Error reload(bool p_keep_state = false) override; +#ifdef TOOLS_ENABLED + virtual const Vector<DocData::ClassDoc> &get_documentation() const override { + static Vector<DocData::ClassDoc> docs; + return docs; + } +#endif // TOOLS_ENABLED + virtual bool is_tool() const override; virtual bool is_valid() const override; @@ -384,7 +391,7 @@ class VisualScriptInstance : public ScriptInstance { Object *owner; Ref<VisualScript> script; - Map<StringName, Variant> variables; //using variable path, not script + Map<StringName, Variant> variables; // Using variable path, not script. Map<int, VisualScriptNodeInstance *> instances; struct Function { @@ -407,9 +414,8 @@ class VisualScriptInstance : public ScriptInstance { void _dependency_step(VisualScriptNodeInstance *node, int p_pass, int *pass_stack, const Variant **input_args, Variant **output_args, Variant *variant_stack, Callable::CallError &r_error, String &error_str, VisualScriptNodeInstance **r_error_node); Variant _call_internal(const StringName &p_method, void *p_stack, int p_stack_size, VisualScriptNodeInstance *p_node, int p_flow_stack_pos, int p_pass, bool p_resuming_yield, Callable::CallError &r_error); - //Map<StringName,Function> functions; - friend class VisualScriptFunctionState; //for yield - friend class VisualScriptLanguage; //for debugger + friend class VisualScriptFunctionState; // For yield. + friend class VisualScriptLanguage; // For debugger. public: virtual bool set(const StringName &p_name, const Variant &p_value); virtual bool get(const StringName &p_name, Variant &r_ret) const; @@ -530,7 +536,7 @@ public: _FORCE_INLINE_ void enter_function(VisualScriptInstance *p_instance, const StringName *p_function, Variant *p_stack, Variant **p_work_mem, int *current_id) { if (Thread::get_main_id() != Thread::get_caller_id()) { - return; //no support for other threads than main for now + return; // No support for other threads than main for now. } if (EngineDebugger::get_script_debugger()->get_lines_left() > 0 && EngineDebugger::get_script_debugger()->get_depth() >= 0) { @@ -538,7 +544,7 @@ public: } if (_debug_call_stack_pos >= _debug_max_call_stack) { - //stack overflow + // Stack overflow. _debug_error = "Stack Overflow (Stack Size: " + itos(_debug_max_call_stack) + ")"; EngineDebugger::get_script_debugger()->debug(this); return; @@ -554,7 +560,7 @@ public: _FORCE_INLINE_ void exit_function() { if (Thread::get_main_id() != Thread::get_caller_id()) { - return; //no support for other threads than main for now + return; // No support for other threads than main for now. } if (EngineDebugger::get_script_debugger()->get_lines_left() > 0 && EngineDebugger::get_script_debugger()->get_depth() >= 0) { @@ -632,7 +638,7 @@ public: ~VisualScriptLanguage(); }; -//aid for registering +// Aid for registering. template <class T> static Ref<VisualScriptNode> create_node_generic(const String &p_name) { Ref<T> node; diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp index fe0c399f8d..2558c1d7ec 100644 --- a/modules/visual_script/visual_script_builtin_funcs.cpp +++ b/modules/visual_script/visual_script_builtin_funcs.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -63,7 +63,7 @@ const char *VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX "is_inf", "ease", "step_decimals", - "stepify", + "snapped", "lerp", "inverse_lerp", "range_lerp", @@ -192,7 +192,7 @@ int VisualScriptBuiltinFunc::get_func_argument_count(BuiltinFunc p_func) { case MATH_POSMOD: case MATH_POW: case MATH_EASE: - case MATH_STEPIFY: + case MATH_SNAPPED: case MATH_RANDF_RANGE: case MATH_RANDI_RANGE: case MATH_POLAR2CARTESIAN: @@ -309,7 +309,7 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const case MATH_STEP_DECIMALS: { return PropertyInfo(Variant::FLOAT, "step"); } break; - case MATH_STEPIFY: { + case MATH_SNAPPED: { if (p_idx == 0) { return PropertyInfo(Variant::FLOAT, "s"); } else { @@ -537,7 +537,7 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons case MATH_STEP_DECIMALS: { t = Variant::INT; } break; - case MATH_STEPIFY: + case MATH_SNAPPED: case MATH_LERP: case MATH_LERP_ANGLE: case MATH_INVERSE_LERP: @@ -805,10 +805,10 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in VALIDATE_ARG_NUM(0); *r_return = Math::step_decimals((double)*p_inputs[0]); } break; - case VisualScriptBuiltinFunc::MATH_STEPIFY: { + case VisualScriptBuiltinFunc::MATH_SNAPPED: { VALIDATE_ARG_NUM(0); VALIDATE_ARG_NUM(1); - *r_return = Math::stepify((double)*p_inputs[0], (double)*p_inputs[1]); + *r_return = Math::snapped((double)*p_inputs[0], (double)*p_inputs[1]); } break; case VisualScriptBuiltinFunc::MATH_LERP: { VALIDATE_ARG_NUM(0); @@ -1254,7 +1254,7 @@ void VisualScriptBuiltinFunc::_bind_methods() { BIND_ENUM_CONSTANT(MATH_ISINF); BIND_ENUM_CONSTANT(MATH_EASE); BIND_ENUM_CONSTANT(MATH_STEP_DECIMALS); - BIND_ENUM_CONSTANT(MATH_STEPIFY); + BIND_ENUM_CONSTANT(MATH_SNAPPED); BIND_ENUM_CONSTANT(MATH_LERP); BIND_ENUM_CONSTANT(MATH_INVERSE_LERP); BIND_ENUM_CONSTANT(MATH_RANGE_LERP); @@ -1344,7 +1344,7 @@ void register_visual_script_builtin_func_node() { VisualScriptLanguage::singleton->add_register_func("functions/built_in/ease", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_EASE>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/step_decimals", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_STEP_DECIMALS>); - VisualScriptLanguage::singleton->add_register_func("functions/built_in/stepify", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_STEPIFY>); + VisualScriptLanguage::singleton->add_register_func("functions/built_in/snapped", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SNAPPED>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/lerp", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_LERP>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/lerp_angle", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_LERP_ANGLE>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/inverse_lerp", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_INVERSE_LERP>); diff --git a/modules/visual_script/visual_script_builtin_funcs.h b/modules/visual_script/visual_script_builtin_funcs.h index 361b445e30..eaa2ef41e2 100644 --- a/modules/visual_script/visual_script_builtin_funcs.h +++ b/modules/visual_script/visual_script_builtin_funcs.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -63,7 +63,7 @@ public: MATH_ISINF, MATH_EASE, MATH_STEP_DECIMALS, - MATH_STEPIFY, + MATH_SNAPPED, MATH_LERP, MATH_INVERSE_LERP, MATH_RANGE_LERP, diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index 64abd3dd84..1e82259e59 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -199,7 +199,7 @@ protected: emit_signal("changed"); } void _var_value_changed() { - _change_notify("value"); //so the whole tree is not redrawn, makes editing smoother in general + _change_notify("value"); // So the whole tree is not redrawn, makes editing smoother in general. emit_signal("changed"); } @@ -227,6 +227,19 @@ protected: undo_redo->create_action(TTR("Set Variable Type")); undo_redo->add_do_method(script.ptr(), "set_variable_info", var, dc); undo_redo->add_undo_method(script.ptr(), "set_variable_info", var, d); + + // Setting the default value. + Variant::Type type = (Variant::Type)(int)p_value; + if (type != Variant::NIL) { + Variant default_value; + Callable::CallError ce; + Variant::construct(type, default_value, nullptr, 0, ce); + if (ce.error == Callable::CallError::CALL_OK) { + undo_redo->add_do_method(script.ptr(), "set_variable_default_value", var, default_value); + undo_redo->add_undo_method(script.ptr(), "set_variable_default_value", var, dc["value"]); + } + } + undo_redo->add_do_method(this, "_var_changed"); undo_redo->add_undo_method(this, "_var_changed"); undo_redo->commit_action(); @@ -309,7 +322,7 @@ protected: } p_list->push_back(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, argt)); p_list->push_back(PropertyInfo(script->get_variable_info(var).type, "value", script->get_variable_info(var).hint, script->get_variable_info(var).hint_string, PROPERTY_USAGE_DEFAULT)); - // Update this when PropertyHint changes + // Update this when PropertyHint changes. p_list->push_back(PropertyInfo(Variant::INT, "hint", PROPERTY_HINT_ENUM, "None,Range,ExpRange,Enum,ExpEasing,Length,SpriteFrame,KeyAccel,Flags,Layers2dRender,Layers2dPhysics,Layer3dRender,Layer3dPhysics,File,Dir,GlobalFile,GlobalDir,ResourceType,MultilineText,PlaceholderText,ColorNoAlpha,ImageCompressLossy,ImageCompressLossLess,ObjectId,String,NodePathToEditedNode,MethodOfVariantType,MethodOfBaseType,MethodOfInstance,MethodOfScript,PropertyOfVariantType,PropertyOfBaseType,PropertyOfInstance,PropertyOfScript,ObjectTooBig,NodePathValidTypes")); p_list->push_back(PropertyInfo(Variant::STRING, "hint_string")); p_list->push_back(PropertyInfo(Variant::BOOL, "export")); @@ -546,39 +559,29 @@ static Color _color_from_type(Variant::Type p_type, bool dark_theme = true) { void VisualScriptEditor::_update_graph_connections() { graph->clear_connections(); - List<StringName> funcs; - script->get_function_list(&funcs); + List<VisualScript::SequenceConnection> sequence_conns; + script->get_sequence_connection_list(&sequence_conns); - if (funcs.size() <= 0) { - updating_graph = false; - return; + for (List<VisualScript::SequenceConnection>::Element *E = sequence_conns.front(); E; E = E->next()) { + graph->connect_node(itos(E->get().from_node), E->get().from_output, itos(E->get().to_node), 0); } - for (List<StringName>::Element *F = funcs.front(); F; F = F->next()) { - List<VisualScript::SequenceConnection> sequence_conns; - script->get_sequence_connection_list(F->get(), &sequence_conns); - - for (List<VisualScript::SequenceConnection>::Element *E = sequence_conns.front(); E; E = E->next()) { - graph->connect_node(itos(E->get().from_node), E->get().from_output, itos(E->get().to_node), 0); - } - - List<VisualScript::DataConnection> data_conns; - script->get_data_connection_list(F->get(), &data_conns); + List<VisualScript::DataConnection> data_conns; + script->get_data_connection_list(&data_conns); - for (List<VisualScript::DataConnection>::Element *E = data_conns.front(); E; E = E->next()) { - VisualScript::DataConnection dc = E->get(); + for (List<VisualScript::DataConnection>::Element *E = data_conns.front(); E; E = E->next()) { + VisualScript::DataConnection dc = E->get(); - Ref<VisualScriptNode> from_node = script->get_node(F->get(), E->get().from_node); - Ref<VisualScriptNode> to_node = script->get_node(F->get(), E->get().to_node); + Ref<VisualScriptNode> from_node = script->get_node(E->get().from_node); + Ref<VisualScriptNode> to_node = script->get_node(E->get().to_node); - if (to_node->has_input_sequence_port()) { - dc.to_port++; - } + if (to_node->has_input_sequence_port()) { + dc.to_port++; + } - dc.from_port += from_node->get_output_sequence_port_count(); + dc.from_port += from_node->get_output_sequence_port_count(); - graph->connect_node(itos(E->get().from_node), dc.from_port, itos(E->get().to_node), dc.to_port); - } + graph->connect_node(itos(E->get().from_node), dc.from_port, itos(E->get().to_node), dc.to_port); } } @@ -605,17 +608,6 @@ void VisualScriptEditor::_update_graph(int p_only_id) { } } } - - List<StringName> funcs; - script->get_function_list(&funcs); - - if (funcs.size() <= 0) { - graph->hide(); - select_func_text->show(); - updating_graph = false; - return; - } - graph->show(); select_func_text->hide(); @@ -655,348 +647,347 @@ void VisualScriptEditor::_update_graph(int p_only_id) { }; Ref<Texture2D> seq_port = Control::get_theme_icon("VisualShaderPort", "EditorIcons"); + List<int> node_ids; + script->get_node_list(&node_ids); - for (List<StringName>::Element *F = funcs.front(); F; F = F->next()) { // loop through all the functions - - List<int> ids; - script->get_node_list(F->get(), &ids); - StringName editor_icons = "EditorIcons"; + List<int> ids; + script->get_node_list(&ids); + StringName editor_icons = "EditorIcons"; - for (List<int>::Element *E = ids.front(); E; E = E->next()) { - if (p_only_id >= 0 && p_only_id != E->get()) { - continue; - } + for (List<int>::Element *E = ids.front(); E; E = E->next()) { + if (p_only_id >= 0 && p_only_id != E->get()) { + continue; + } - Ref<VisualScriptNode> node = script->get_node(F->get(), E->get()); - Vector2 pos = script->get_node_position(F->get(), E->get()); + Ref<VisualScriptNode> node = script->get_node(E->get()); + Vector2 pos = script->get_node_position(E->get()); - GraphNode *gnode = memnew(GraphNode); - gnode->set_title(node->get_caption()); - gnode->set_offset(pos * EDSCALE); - if (error_line == E->get()) { - gnode->set_overlay(GraphNode::OVERLAY_POSITION); - } else if (node->is_breakpoint()) { - gnode->set_overlay(GraphNode::OVERLAY_BREAKPOINT); - } + GraphNode *gnode = memnew(GraphNode); + gnode->set_title(node->get_caption()); + gnode->set_position_offset(pos * EDSCALE); + if (error_line == E->get()) { + gnode->set_overlay(GraphNode::OVERLAY_POSITION); + } else if (node->is_breakpoint()) { + gnode->set_overlay(GraphNode::OVERLAY_BREAKPOINT); + } - gnode->set_meta("__vnode", node); - gnode->set_name(itos(E->get())); - gnode->connect("dragged", callable_mp(this, &VisualScriptEditor::_node_moved), varray(E->get())); - gnode->connect("close_request", callable_mp(this, &VisualScriptEditor::_remove_node), varray(E->get()), CONNECT_DEFERRED); + gnode->set_meta("__vnode", node); + gnode->set_name(itos(E->get())); + gnode->connect("dragged", callable_mp(this, &VisualScriptEditor::_node_moved), varray(E->get())); + gnode->connect("close_request", callable_mp(this, &VisualScriptEditor::_remove_node), varray(E->get()), CONNECT_DEFERRED); - if (E->get() != script->get_function_node_id(F->get())) { - //function can't be erased + { + Ref<VisualScriptFunction> v = node; + if (!v.is_valid()) { gnode->set_show_close_button(true); } + } - bool has_gnode_text = false; + bool has_gnode_text = false; - Ref<VisualScriptLists> nd_list = node; - bool is_vslist = nd_list.is_valid(); - if (is_vslist) { - HBoxContainer *hbnc = memnew(HBoxContainer); + Ref<VisualScriptLists> nd_list = node; + bool is_vslist = nd_list.is_valid(); + if (is_vslist) { + HBoxContainer *hbnc = memnew(HBoxContainer); + if (nd_list->is_input_port_editable()) { + has_gnode_text = true; + Button *btn = memnew(Button); + btn->set_text(TTR("Add Input Port")); + hbnc->add_child(btn); + btn->connect("pressed", callable_mp(this, &VisualScriptEditor::_add_input_port), varray(E->get()), CONNECT_DEFERRED); + } + if (nd_list->is_output_port_editable()) { if (nd_list->is_input_port_editable()) { - has_gnode_text = true; - Button *btn = memnew(Button); - btn->set_text(TTR("Add Input Port")); - hbnc->add_child(btn); - btn->connect("pressed", callable_mp(this, &VisualScriptEditor::_add_input_port), varray(E->get()), CONNECT_DEFERRED); - } - if (nd_list->is_output_port_editable()) { - if (nd_list->is_input_port_editable()) { - hbnc->add_spacer(); - } - has_gnode_text = true; - Button *btn = memnew(Button); - btn->set_text(TTR("Add Output Port")); - hbnc->add_child(btn); - btn->connect("pressed", callable_mp(this, &VisualScriptEditor::_add_output_port), varray(E->get()), CONNECT_DEFERRED); + hbnc->add_spacer(); } - gnode->add_child(hbnc); - } else if (Object::cast_to<VisualScriptExpression>(node.ptr())) { has_gnode_text = true; - LineEdit *line_edit = memnew(LineEdit); - line_edit->set_text(node->get_text()); - line_edit->set_expand_to_text_length(true); - line_edit->add_theme_font_override("font", get_theme_font("source", "EditorFonts")); - line_edit->add_theme_font_size_override("font_size", get_theme_font_size("source_size", "EditorFonts")); - gnode->add_child(line_edit); - line_edit->connect("text_changed", callable_mp(this, &VisualScriptEditor::_expression_text_changed), varray(E->get())); - } else { - String text = node->get_text(); - if (!text.empty()) { - has_gnode_text = true; - Label *label = memnew(Label); - label->set_text(text); - gnode->add_child(label); - } + Button *btn = memnew(Button); + btn->set_text(TTR("Add Output Port")); + hbnc->add_child(btn); + btn->connect("pressed", callable_mp(this, &VisualScriptEditor::_add_output_port), varray(E->get()), CONNECT_DEFERRED); } - - if (Object::cast_to<VisualScriptComment>(node.ptr())) { - Ref<VisualScriptComment> vsc = node; - gnode->set_comment(true); - gnode->set_resizable(true); - gnode->set_custom_minimum_size(vsc->get_size() * EDSCALE); - gnode->connect("resize_request", callable_mp(this, &VisualScriptEditor::_comment_node_resized), varray(E->get())); + gnode->add_child(hbnc); + } else if (Object::cast_to<VisualScriptExpression>(node.ptr())) { + has_gnode_text = true; + LineEdit *line_edit = memnew(LineEdit); + line_edit->set_text(node->get_text()); + line_edit->set_expand_to_text_length(true); + line_edit->add_theme_font_override("font", get_theme_font("source", "EditorFonts")); + gnode->add_child(line_edit); + line_edit->connect("text_changed", callable_mp(this, &VisualScriptEditor::_expression_text_changed), varray(E->get())); + } else { + String text = node->get_text(); + if (!text.is_empty()) { + has_gnode_text = true; + Label *label = memnew(Label); + label->set_text(text); + gnode->add_child(label); } + } - if (node_styles.has(node->get_category())) { - Ref<StyleBoxFlat> sbf = node_styles[node->get_category()]; - if (gnode->is_comment()) { - sbf = EditorNode::get_singleton()->get_theme_base()->get_theme()->get_stylebox("comment", "GraphNode"); - } + if (Object::cast_to<VisualScriptComment>(node.ptr())) { + Ref<VisualScriptComment> vsc = node; + gnode->set_comment(true); + gnode->set_resizable(true); + gnode->set_custom_minimum_size(vsc->get_size() * EDSCALE); + gnode->connect("resize_request", callable_mp(this, &VisualScriptEditor::_comment_node_resized), varray(E->get())); + } - Color c = sbf->get_border_color(); - Color ic = c; - c.a = 1; - if (EditorSettings::get_singleton()->get("interface/theme/use_graph_node_headers")) { - Color mono_color; - if (((c.r + c.g + c.b) / 3) < 0.7) { - mono_color = Color(1.0, 1.0, 1.0); - ic = Color(0.0, 0.0, 0.0, 0.7); - } else { - mono_color = Color(0.0, 0.0, 0.0); - ic = Color(1.0, 1.0, 1.0, 0.7); - } - mono_color.a = 0.85; - c = mono_color; + if (node_styles.has(node->get_category())) { + Ref<StyleBoxFlat> sbf = node_styles[node->get_category()]; + if (gnode->is_comment()) { + sbf = EditorNode::get_singleton()->get_theme_base()->get_theme()->get_stylebox("comment", "GraphNode"); + } + + Color c = sbf->get_border_color(); + Color ic = c; + c.a = 1; + if (EditorSettings::get_singleton()->get("interface/theme/use_graph_node_headers")) { + Color mono_color; + if (((c.r + c.g + c.b) / 3) < 0.7) { + mono_color = Color(1.0, 1.0, 1.0); + ic = Color(0.0, 0.0, 0.0, 0.7); + } else { + mono_color = Color(0.0, 0.0, 0.0); + ic = Color(1.0, 1.0, 1.0, 0.7); } - gnode->add_theme_color_override("title_color", c); - c.a = 0.7; - gnode->add_theme_color_override("close_color", c); - gnode->add_theme_color_override("resizer_color", ic); - gnode->add_theme_style_override("frame", sbf); + mono_color.a = 0.85; + c = mono_color; } + gnode->add_theme_color_override("title_color", c); + c.a = 0.7; + gnode->add_theme_color_override("close_color", c); + gnode->add_theme_color_override("resizer_color", ic); + gnode->add_theme_style_override("frame", sbf); + } - const Color mono_color = get_theme_color("mono_color", "Editor"); + const Color mono_color = get_theme_color("mono_color", "Editor"); - int slot_idx = 0; + int slot_idx = 0; - bool single_seq_output = node->get_output_sequence_port_count() == 1 && node->get_output_sequence_port_text(0) == String(); - if ((node->has_input_sequence_port() || single_seq_output) || has_gnode_text) { - // IF has_gnode_text is true BUT we have no sequence ports to draw (in here), - // we still draw the disabled default ones to shift up the slots by one, - // so the slots DON'T start with the content text. + bool single_seq_output = node->get_output_sequence_port_count() == 1 && node->get_output_sequence_port_text(0) == String(); + if ((node->has_input_sequence_port() || single_seq_output) || has_gnode_text) { + // IF has_gnode_text is true BUT we have no sequence ports to draw (in here), + // we still draw the disabled default ones to shift up the slots by one, + // so the slots DON'T start with the content text. - // IF has_gnode_text is false, but we DO want to draw default sequence ports, - // we draw a dummy text to take up the position of the sequence nodes, so all the other ports are still aligned correctly. - if (!has_gnode_text) { - Label *dummy = memnew(Label); - dummy->set_text(" "); - gnode->add_child(dummy); - } - gnode->set_slot(0, node->has_input_sequence_port(), TYPE_SEQUENCE, mono_color, single_seq_output, TYPE_SEQUENCE, mono_color, seq_port, seq_port); - slot_idx++; + // IF has_gnode_text is false, but we DO want to draw default sequence ports, + // we draw a dummy text to take up the position of the sequence nodes, so all the other ports are still aligned correctly. + if (!has_gnode_text) { + Label *dummy = memnew(Label); + dummy->set_text(" "); + gnode->add_child(dummy); } + gnode->set_slot(0, node->has_input_sequence_port(), TYPE_SEQUENCE, mono_color, single_seq_output, TYPE_SEQUENCE, mono_color, seq_port, seq_port); + slot_idx++; + } - int mixed_seq_ports = 0; + int mixed_seq_ports = 0; - if (!single_seq_output) { - if (node->has_mixed_input_and_sequence_ports()) { - mixed_seq_ports = node->get_output_sequence_port_count(); - } else { - for (int i = 0; i < node->get_output_sequence_port_count(); i++) { - Label *text2 = memnew(Label); - text2->set_text(node->get_output_sequence_port_text(i)); - text2->set_align(Label::ALIGN_RIGHT); - gnode->add_child(text2); - gnode->set_slot(slot_idx, false, 0, Color(), true, TYPE_SEQUENCE, mono_color, seq_port, seq_port); - slot_idx++; - } + if (!single_seq_output) { + if (node->has_mixed_input_and_sequence_ports()) { + mixed_seq_ports = node->get_output_sequence_port_count(); + } else { + for (int i = 0; i < node->get_output_sequence_port_count(); i++) { + Label *text2 = memnew(Label); + text2->set_text(node->get_output_sequence_port_text(i)); + text2->set_align(Label::ALIGN_RIGHT); + gnode->add_child(text2); + gnode->set_slot(slot_idx, false, 0, Color(), true, TYPE_SEQUENCE, mono_color, seq_port, seq_port); + slot_idx++; } } + } - for (int i = 0; i < MAX(node->get_output_value_port_count(), MAX(mixed_seq_ports, node->get_input_value_port_count())); i++) { - bool left_ok = false; - Variant::Type left_type = Variant::NIL; - String left_name; + for (int i = 0; i < MAX(node->get_output_value_port_count(), MAX(mixed_seq_ports, node->get_input_value_port_count())); i++) { + bool left_ok = false; + Variant::Type left_type = Variant::NIL; + String left_name; - if (i < node->get_input_value_port_count()) { - PropertyInfo pi = node->get_input_value_port_info(i); - left_ok = true; - left_type = pi.type; - left_name = pi.name; - } + if (i < node->get_input_value_port_count()) { + PropertyInfo pi = node->get_input_value_port_info(i); + left_ok = true; + left_type = pi.type; + left_name = pi.name; + } - bool right_ok = false; - Variant::Type right_type = Variant::NIL; - String right_name; + bool right_ok = false; + Variant::Type right_type = Variant::NIL; + String right_name; - if (i >= mixed_seq_ports && i < node->get_output_value_port_count() + mixed_seq_ports) { - PropertyInfo pi = node->get_output_value_port_info(i - mixed_seq_ports); - right_ok = true; - right_type = pi.type; - right_name = pi.name; + if (i >= mixed_seq_ports && i < node->get_output_value_port_count() + mixed_seq_ports) { + PropertyInfo pi = node->get_output_value_port_info(i - mixed_seq_ports); + right_ok = true; + right_type = pi.type; + right_name = pi.name; + } + VBoxContainer *vbc = memnew(VBoxContainer); + HBoxContainer *hbc = memnew(HBoxContainer); + HBoxContainer *hbc2 = memnew(HBoxContainer); + vbc->add_child(hbc); + vbc->add_child(hbc2); + if (left_ok) { + Ref<Texture2D> t; + if (left_type >= 0 && left_type < Variant::VARIANT_MAX) { + t = type_icons[left_type]; + } + if (t.is_valid()) { + TextureRect *tf = memnew(TextureRect); + tf->set_texture(t); + tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED); + hbc->add_child(tf); } - VBoxContainer *vbc = memnew(VBoxContainer); - HBoxContainer *hbc = memnew(HBoxContainer); - HBoxContainer *hbc2 = memnew(HBoxContainer); - vbc->add_child(hbc); - vbc->add_child(hbc2); - if (left_ok) { - Ref<Texture2D> t; - if (left_type >= 0 && left_type < Variant::VARIANT_MAX) { - t = type_icons[left_type]; - } - if (t.is_valid()) { - TextureRect *tf = memnew(TextureRect); - tf->set_texture(t); - tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED); - hbc->add_child(tf); - } - - if (is_vslist) { - if (nd_list->is_input_port_name_editable()) { - LineEdit *name_box = memnew(LineEdit); - hbc->add_child(name_box); - name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0)); - name_box->set_text(left_name); - name_box->set_expand_to_text_length(true); - name_box->connect("resized", callable_mp(this, &VisualScriptEditor::_update_node_size), varray(E->get())); - name_box->connect("focus_exited", callable_mp(this, &VisualScriptEditor::_port_name_focus_out), varray(name_box, E->get(), i, true)); - } else { - hbc->add_child(memnew(Label(left_name))); - } - - if (nd_list->is_input_port_type_editable()) { - OptionButton *opbtn = memnew(OptionButton); - for (int j = Variant::NIL; j < Variant::VARIANT_MAX; j++) { - opbtn->add_item(Variant::get_type_name(Variant::Type(j))); - } - opbtn->select(left_type); - opbtn->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); - hbc->add_child(opbtn); - opbtn->connect("item_selected", callable_mp(this, &VisualScriptEditor::_change_port_type), varray(E->get(), i, true), CONNECT_DEFERRED); - } - Button *rmbtn = memnew(Button); - rmbtn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("Remove", "EditorIcons")); - hbc->add_child(rmbtn); - rmbtn->connect("pressed", callable_mp(this, &VisualScriptEditor::_remove_input_port), varray(E->get(), i), CONNECT_DEFERRED); + if (is_vslist) { + if (nd_list->is_input_port_name_editable()) { + LineEdit *name_box = memnew(LineEdit); + hbc->add_child(name_box); + name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0)); + name_box->set_text(left_name); + name_box->set_expand_to_text_length(true); + name_box->connect("resized", callable_mp(this, &VisualScriptEditor::_update_node_size), varray(E->get())); + name_box->connect("focus_exited", callable_mp(this, &VisualScriptEditor::_port_name_focus_out), varray(name_box, E->get(), i, true)); } else { hbc->add_child(memnew(Label(left_name))); } - if (left_type != Variant::NIL && !script->is_input_value_port_connected(F->get(), E->get(), i)) { - PropertyInfo pi = node->get_input_value_port_info(i); - Button *button = memnew(Button); - Variant value = node->get_default_input_value(i); - if (value.get_type() != left_type) { - //different type? for now convert - //not the same, reconvert - Callable::CallError ce; - const Variant *existingp = &value; - Variant::construct(left_type, value, &existingp, 1, ce); + if (nd_list->is_input_port_type_editable()) { + OptionButton *opbtn = memnew(OptionButton); + for (int j = Variant::NIL; j < Variant::VARIANT_MAX; j++) { + opbtn->add_item(Variant::get_type_name(Variant::Type(j))); } - - if (left_type == Variant::COLOR) { - button->set_custom_minimum_size(Size2(30, 0) * EDSCALE); - button->connect("draw", callable_mp(this, &VisualScriptEditor::_draw_color_over_button), varray(button, value)); - } else if (left_type == Variant::OBJECT && Ref<Resource>(value).is_valid()) { - Ref<Resource> res = value; - Array arr; - arr.push_back(button->get_instance_id()); - arr.push_back(String(value)); - EditorResourcePreview::get_singleton()->queue_edited_resource_preview(res, this, "_button_resource_previewed", arr); - - } else if (pi.type == Variant::INT && pi.hint == PROPERTY_HINT_ENUM) { - button->set_text(pi.hint_string.get_slice(",", value)); - } else { - button->set_text(value); - } - button->connect("pressed", callable_mp(this, &VisualScriptEditor::_default_value_edited), varray(button, E->get(), i)); - hbc2->add_child(button); + opbtn->select(left_type); + opbtn->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); + hbc->add_child(opbtn); + opbtn->connect("item_selected", callable_mp(this, &VisualScriptEditor::_change_port_type), varray(E->get(), i, true), CONNECT_DEFERRED); } + + Button *rmbtn = memnew(Button); + rmbtn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("Remove", "EditorIcons")); + hbc->add_child(rmbtn); + rmbtn->connect("pressed", callable_mp(this, &VisualScriptEditor::_remove_input_port), varray(E->get(), i), CONNECT_DEFERRED); } else { - Control *c = memnew(Control); - c->set_custom_minimum_size(Size2(10, 0) * EDSCALE); - hbc->add_child(c); + hbc->add_child(memnew(Label(left_name))); } - hbc->add_spacer(); - hbc2->add_spacer(); + if (left_type != Variant::NIL && !script->is_input_value_port_connected(E->get(), i)) { + PropertyInfo pi = node->get_input_value_port_info(i); + Button *button = memnew(Button); + Variant value = node->get_default_input_value(i); + if (value.get_type() != left_type) { + //different type? for now convert + //not the same, reconvert + Callable::CallError ce; + const Variant *existingp = &value; + Variant::construct(left_type, value, &existingp, 1, ce); + } - if (i < mixed_seq_ports) { - Label *text2 = memnew(Label); - text2->set_text(node->get_output_sequence_port_text(i)); - text2->set_align(Label::ALIGN_RIGHT); - hbc->add_child(text2); + if (left_type == Variant::COLOR) { + button->set_custom_minimum_size(Size2(30, 0) * EDSCALE); + button->connect("draw", callable_mp(this, &VisualScriptEditor::_draw_color_over_button), varray(button, value)); + } else if (left_type == Variant::OBJECT && Ref<Resource>(value).is_valid()) { + Ref<Resource> res = value; + Array arr; + arr.push_back(button->get_instance_id()); + arr.push_back(String(value)); + EditorResourcePreview::get_singleton()->queue_edited_resource_preview(res, this, "_button_resource_previewed", arr); + + } else if (pi.type == Variant::INT && pi.hint == PROPERTY_HINT_ENUM) { + button->set_text(pi.hint_string.get_slice(",", value)); + } else { + button->set_text(value); + } + button->connect("pressed", callable_mp(this, &VisualScriptEditor::_default_value_edited), varray(button, E->get(), i)); + hbc2->add_child(button); } + } else { + Control *c = memnew(Control); + c->set_custom_minimum_size(Size2(10, 0) * EDSCALE); + hbc->add_child(c); + } - if (right_ok) { - if (is_vslist) { - Button *rmbtn = memnew(Button); - rmbtn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("Remove", "EditorIcons")); - hbc->add_child(rmbtn); - rmbtn->connect("pressed", callable_mp(this, &VisualScriptEditor::_remove_output_port), varray(E->get(), i), CONNECT_DEFERRED); - - if (nd_list->is_output_port_type_editable()) { - OptionButton *opbtn = memnew(OptionButton); - for (int j = Variant::NIL; j < Variant::VARIANT_MAX; j++) { - opbtn->add_item(Variant::get_type_name(Variant::Type(j))); - } - opbtn->select(right_type); - opbtn->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); - hbc->add_child(opbtn); - opbtn->connect("item_selected", callable_mp(this, &VisualScriptEditor::_change_port_type), varray(E->get(), i, false), CONNECT_DEFERRED); - } + hbc->add_spacer(); + hbc2->add_spacer(); + + if (i < mixed_seq_ports) { + Label *text2 = memnew(Label); + text2->set_text(node->get_output_sequence_port_text(i)); + text2->set_align(Label::ALIGN_RIGHT); + hbc->add_child(text2); + } - if (nd_list->is_output_port_name_editable()) { - LineEdit *name_box = memnew(LineEdit); - hbc->add_child(name_box); - name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0)); - name_box->set_text(right_name); - name_box->set_expand_to_text_length(true); - name_box->connect("resized", callable_mp(this, &VisualScriptEditor::_update_node_size), varray(E->get())); - name_box->connect("focus_exited", callable_mp(this, &VisualScriptEditor::_port_name_focus_out), varray(name_box, E->get(), i, false)); - } else { - hbc->add_child(memnew(Label(right_name))); + if (right_ok) { + if (is_vslist) { + Button *rmbtn = memnew(Button); + rmbtn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("Remove", "EditorIcons")); + hbc->add_child(rmbtn); + rmbtn->connect("pressed", callable_mp(this, &VisualScriptEditor::_remove_output_port), varray(E->get(), i), CONNECT_DEFERRED); + + if (nd_list->is_output_port_type_editable()) { + OptionButton *opbtn = memnew(OptionButton); + for (int j = Variant::NIL; j < Variant::VARIANT_MAX; j++) { + opbtn->add_item(Variant::get_type_name(Variant::Type(j))); } - } else { - hbc->add_child(memnew(Label(right_name))); + opbtn->select(right_type); + opbtn->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); + hbc->add_child(opbtn); + opbtn->connect("item_selected", callable_mp(this, &VisualScriptEditor::_change_port_type), varray(E->get(), i, false), CONNECT_DEFERRED); } - Ref<Texture2D> t; - if (right_type >= 0 && right_type < Variant::VARIANT_MAX) { - t = type_icons[right_type]; - } - if (t.is_valid()) { - TextureRect *tf = memnew(TextureRect); - tf->set_texture(t); - tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED); - hbc->add_child(tf); + if (nd_list->is_output_port_name_editable()) { + LineEdit *name_box = memnew(LineEdit); + hbc->add_child(name_box); + name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0)); + name_box->set_text(right_name); + name_box->set_expand_to_text_length(true); + name_box->connect("resized", callable_mp(this, &VisualScriptEditor::_update_node_size), varray(E->get())); + name_box->connect("focus_exited", callable_mp(this, &VisualScriptEditor::_port_name_focus_out), varray(name_box, E->get(), i, false)); + } else { + hbc->add_child(memnew(Label(right_name))); } - } - - gnode->add_child(vbc); - - bool dark_theme = get_theme_constant("dark_theme", "Editor"); - if (i < mixed_seq_ports) { - gnode->set_slot(slot_idx, left_ok, left_type, _color_from_type(left_type, dark_theme), true, TYPE_SEQUENCE, mono_color, Ref<Texture2D>(), seq_port); } else { - gnode->set_slot(slot_idx, left_ok, left_type, _color_from_type(left_type, dark_theme), right_ok, right_type, _color_from_type(right_type, dark_theme)); + hbc->add_child(memnew(Label(right_name))); } - slot_idx++; + Ref<Texture2D> t; + if (right_type >= 0 && right_type < Variant::VARIANT_MAX) { + t = type_icons[right_type]; + } + if (t.is_valid()) { + TextureRect *tf = memnew(TextureRect); + tf->set_texture(t); + tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED); + hbc->add_child(tf); + } } - graph->add_child(gnode); + gnode->add_child(vbc); - if (gnode->is_comment()) { - graph->move_child(gnode, 0); + bool dark_theme = get_theme_constant("dark_theme", "Editor"); + if (i < mixed_seq_ports) { + gnode->set_slot(slot_idx, left_ok, left_type, _color_from_type(left_type, dark_theme), true, TYPE_SEQUENCE, mono_color, Ref<Texture2D>(), seq_port); + } else { + gnode->set_slot(slot_idx, left_ok, left_type, _color_from_type(left_type, dark_theme), right_ok, right_type, _color_from_type(right_type, dark_theme)); } + + slot_idx++; + } + + graph->add_child(gnode); + + if (gnode->is_comment()) { + graph->move_child(gnode, 0); } } + _update_graph_connections(); - // use default_func instead of default_func for now I think that should be good stop gap solution to ensure not breaking anything - graph->call_deferred("set_scroll_ofs", script->get_function_scroll(default_func) * EDSCALE); + // Use default_func instead of default_func for now I think that should be good stop gap solution to ensure not breaking anything. + graph->call_deferred("set_scroll_ofs", script->get_scroll() * EDSCALE); updating_graph = false; } void VisualScriptEditor::_change_port_type(int p_select, int p_id, int p_port, bool is_input) { - StringName func = _get_function_of_node(p_id); - - Ref<VisualScriptLists> vsn = script->get_node(func, p_id); + Ref<VisualScriptLists> vsn = script->get_node(p_id); if (!vsn.is_valid()) { return; } @@ -1015,14 +1006,12 @@ void VisualScriptEditor::_change_port_type(int p_select, int p_id, int p_port, b void VisualScriptEditor::_update_node_size(int p_id) { Node *node = graph->get_node(itos(p_id)); if (Object::cast_to<Control>(node)) { - Object::cast_to<Control>(node)->set_size(Vector2(1, 1)); //shrink if text is smaller + Object::cast_to<Control>(node)->set_size(Vector2(1, 1)); // Shrink if text is smaller. } } void VisualScriptEditor::_port_name_focus_out(const Node *p_name_box, int p_id, int p_port, bool is_input) { - StringName func = _get_function_of_node(p_id); - - Ref<VisualScriptLists> vsn = script->get_node(func, p_id); + Ref<VisualScriptLists> vsn = script->get_node(p_id); if (!vsn.is_valid()) { return; } @@ -1063,11 +1052,8 @@ void VisualScriptEditor::_update_members() { List<StringName> func_names; script->get_function_list(&func_names); + func_names.sort_custom<StringName::AlphCompare>(); for (List<StringName>::Element *E = func_names.front(); E; E = E->next()) { - if (E->get() == default_func) { - continue; - } - TreeItem *ti = members->create_item(functions); ti->set_text(0, E->get()); ti->set_selectable(0, true); @@ -1200,7 +1186,7 @@ void VisualScriptEditor::_member_selected() { #endif if (held_ctrl) { ERR_FAIL_COND(!script->has_function(selected)); - _center_on_node(selected, script->get_function_node_id(selected)); + _center_on_node(script->get_function_node_id(selected)); } } } @@ -1243,8 +1229,8 @@ void VisualScriptEditor::_member_edited() { int node_id = script->get_function_node_id(name); Ref<VisualScriptFunction> func; - if (script->has_node(name, node_id)) { - func = script->get_node(name, node_id); + if (script->has_node(node_id)) { + func = script->get_node(node_id); } undo_redo->create_action(TTR("Rename Function")); undo_redo->add_do_method(script.ptr(), "rename_function", name, new_name); @@ -1254,21 +1240,17 @@ void VisualScriptEditor::_member_edited() { undo_redo->add_undo_method(func.ptr(), "set_name", name); } - // also fix all function calls - List<StringName> flst; - script->get_function_list(&flst); - for (List<StringName>::Element *E = flst.front(); E; E = E->next()) { - List<int> lst; - script->get_node_list(E->get(), &lst); - for (List<int>::Element *F = lst.front(); F; F = F->next()) { - Ref<VisualScriptFunctionCall> fncall = script->get_node(E->get(), F->get()); - if (!fncall.is_valid()) { - continue; - } - if (fncall->get_function() == name) { - undo_redo->add_do_method(fncall.ptr(), "set_function", new_name); - undo_redo->add_undo_method(fncall.ptr(), "set_function", name); - } + // Also fix all function calls. + List<int> lst; + script->get_node_list(&lst); + for (List<int>::Element *F = lst.front(); F; F = F->next()) { + Ref<VisualScriptFunctionCall> fncall = script->get_node(F->get()); + if (!fncall.is_valid()) { + continue; + } + if (fncall->get_function() == name) { + undo_redo->add_do_method(fncall.ptr(), "set_function", new_name); + undo_redo->add_undo_method(fncall.ptr(), "set_function", name); } } @@ -1280,7 +1262,7 @@ void VisualScriptEditor::_member_edited() { undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed"); undo_redo->commit_action(); - return; //or crash because it will become invalid + return; // Or crash because it will become invalid. } if (ti->get_parent() == root->get_children()->get_next()) { @@ -1296,7 +1278,7 @@ void VisualScriptEditor::_member_edited() { undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed"); undo_redo->commit_action(); - return; //or crash because it will become invalid + return; // Or crash because it will become invalid. } if (ti->get_parent() == root->get_children()->get_next()->get_next()) { @@ -1310,7 +1292,7 @@ void VisualScriptEditor::_member_edited() { undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed"); undo_redo->commit_action(); - return; //or crash because it will become invalid + return; // Or crash because it will become invalid. } } @@ -1344,10 +1326,13 @@ void VisualScriptEditor::_create_function() { func_node->add_argument(arg_type, arg_name); } + int func_node_id = script->get_available_id(); + undo_redo->create_action(TTR("Add Function")); - undo_redo->add_do_method(script.ptr(), "add_function", name); - undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id(), func_node, ofs); + undo_redo->add_do_method(script.ptr(), "add_function", name, func_node_id); undo_redo->add_undo_method(script.ptr(), "remove_function", name); + undo_redo->add_do_method(script.ptr(), "add_node", func_node_id, func_node, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", func_node_id); undo_redo->add_do_method(this, "_update_members"); undo_redo->add_undo_method(this, "_update_members"); undo_redo->add_do_method(this, "_update_graph"); @@ -1431,11 +1416,12 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt if (ti->get_parent() == root) { //main buttons if (ti == root->get_children()) { - //add function, this one uses menu + // Add function, this one uses menu. if (p_button == 1) { + // Ensure script base exists otherwise use custom base type. + ERR_FAIL_COND(script.is_null()); new_virtual_method_select->select_method_from_base_type(script->get_instance_base_type(), String(), true); - return; } else if (p_button == 0) { String name = _validate_name("new_function"); @@ -1445,11 +1431,13 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt Ref<VisualScriptFunction> func_node; func_node.instance(); func_node->set_name(name); + int fn_id = script->get_available_id(); undo_redo->create_action(TTR("Add Function")); - undo_redo->add_do_method(script.ptr(), "add_function", name); - undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id(), func_node, ofs); + undo_redo->add_do_method(script.ptr(), "add_function", name, fn_id); + undo_redo->add_do_method(script.ptr(), "add_node", fn_id, func_node, ofs); undo_redo->add_undo_method(script.ptr(), "remove_function", name); + undo_redo->add_do_method(script.ptr(), "remove_node", fn_id); undo_redo->add_do_method(this, "_update_members"); undo_redo->add_undo_method(this, "_update_members"); undo_redo->add_do_method(this, "_update_graph"); @@ -1461,11 +1449,11 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt _update_graph(); } - return; //or crash because it will become invalid + return; // Or crash because it will become invalid. } if (ti == root->get_children()->get_next()) { - //add variable + // Add variable. String name = _validate_name("new_variable"); selected = name; @@ -1477,11 +1465,11 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt undo_redo->add_do_method(this, "emit_signal", "edited_script_changed"); undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed"); undo_redo->commit_action(); - return; //or crash because it will become invalid + return; // Or crash because it will become invalid. } if (ti == root->get_children()->get_next()->get_next()) { - //add variable + // Add variable. String name = _validate_name("new_signal"); selected = name; @@ -1493,7 +1481,7 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt undo_redo->add_do_method(this, "emit_signal", "edited_script_changed"); undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed"); undo_redo->commit_action(); - return; //or crash because it will become invalid + return; // Or crash because it will become invalid. } } else if (ti->get_parent() == root->get_children()) { selected = ti->get_text(0); @@ -1505,9 +1493,7 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt } void VisualScriptEditor::_add_input_port(int p_id) { - StringName func = _get_function_of_node(p_id); - - Ref<VisualScriptLists> vsn = script->get_node(func, p_id); + Ref<VisualScriptLists> vsn = script->get_node(p_id); if (!vsn.is_valid()) { return; } @@ -1527,9 +1513,7 @@ void VisualScriptEditor::_add_input_port(int p_id) { } void VisualScriptEditor::_add_output_port(int p_id) { - StringName func = _get_function_of_node(p_id); - - Ref<VisualScriptLists> vsn = script->get_node(func, p_id); + Ref<VisualScriptLists> vsn = script->get_node(p_id); if (!vsn.is_valid()) { return; } @@ -1549,9 +1533,7 @@ void VisualScriptEditor::_add_output_port(int p_id) { } void VisualScriptEditor::_remove_input_port(int p_id, int p_port) { - StringName func = _get_function_of_node(p_id); - - Ref<VisualScriptLists> vsn = script->get_node(func, p_id); + Ref<VisualScriptLists> vsn = script->get_node(p_id); if (!vsn.is_valid()) { return; } @@ -1561,17 +1543,17 @@ void VisualScriptEditor::_remove_input_port(int p_id, int p_port) { undo_redo->create_action(TTR("Remove Input Port"), UndoRedo::MERGE_ENDS); int conn_from = -1, conn_port = -1; - script->get_input_value_port_connection_source(func, p_id, p_port, &conn_from, &conn_port); + script->get_input_value_port_connection_source(p_id, p_port, &conn_from, &conn_port); if (conn_from != -1) { - undo_redo->add_do_method(script.ptr(), "data_disconnect", func, conn_from, conn_port, p_id, p_port); + undo_redo->add_do_method(script.ptr(), "data_disconnect", conn_from, conn_port, p_id, p_port); } undo_redo->add_do_method(vsn.ptr(), "remove_input_data_port", p_port); undo_redo->add_do_method(this, "_update_graph", p_id); if (conn_from != -1) { - undo_redo->add_undo_method(script.ptr(), "data_connect", func, conn_from, conn_port, p_id, p_port); + undo_redo->add_undo_method(script.ptr(), "data_connect", conn_from, conn_port, p_id, p_port); } undo_redo->add_undo_method(vsn.ptr(), "add_input_data_port", vsn->get_input_value_port_info(p_port).type, vsn->get_input_value_port_info(p_port).name, p_port); @@ -1583,9 +1565,7 @@ void VisualScriptEditor::_remove_input_port(int p_id, int p_port) { } void VisualScriptEditor::_remove_output_port(int p_id, int p_port) { - StringName func = _get_function_of_node(p_id); - - Ref<VisualScriptLists> vsn = script->get_node(func, p_id); + Ref<VisualScriptLists> vsn = script->get_node(p_id); if (!vsn.is_valid()) { return; } @@ -1595,12 +1575,12 @@ void VisualScriptEditor::_remove_output_port(int p_id, int p_port) { undo_redo->create_action(TTR("Remove Output Port"), UndoRedo::MERGE_ENDS); List<VisualScript::DataConnection> data_connections; - script->get_data_connection_list(func, &data_connections); + script->get_data_connection_list(&data_connections); HashMap<int, Set<int>> conn_map; for (const List<VisualScript::DataConnection>::Element *E = data_connections.front(); E; E = E->next()) { if (E->get().from_node == p_id && E->get().from_port == p_port) { - // push into the connections map + // Push into the connections map. if (!conn_map.has(E->get().to_node)) { conn_map.set(E->get().to_node, Set<int>()); } @@ -1615,7 +1595,7 @@ void VisualScriptEditor::_remove_output_port(int p_id, int p_port) { conn_map.get_key_list(&keys); for (const List<int>::Element *E = keys.front(); E; E = E->next()) { for (const Set<int>::Element *F = conn_map[E->get()].front(); F; F = F->next()) { - undo_redo->add_undo_method(script.ptr(), "data_connect", func, p_id, p_port, E->get(), F->get()); + undo_redo->add_undo_method(script.ptr(), "data_connect", p_id, p_port, E->get(), F->get()); } } @@ -1628,9 +1608,7 @@ void VisualScriptEditor::_remove_output_port(int p_id, int p_port) { } void VisualScriptEditor::_expression_text_changed(const String &p_text, int p_id) { - StringName func = _get_function_of_node(p_id); - - Ref<VisualScriptExpression> vse = script->get_node(func, p_id); + Ref<VisualScriptExpression> vse = script->get_node(p_id); if (!vse.is_valid()) { return; } @@ -1646,7 +1624,7 @@ void VisualScriptEditor::_expression_text_changed(const String &p_text, int p_id Node *node = graph->get_node(itos(p_id)); if (Object::cast_to<Control>(node)) { - Object::cast_to<Control>(node)->set_size(Vector2(1, 1)); //shrink if text is smaller + Object::cast_to<Control>(node)->set_size(Vector2(1, 1)); // Shrink if text is smaller. } updating_graph = false; @@ -1666,19 +1644,14 @@ Vector2 VisualScriptEditor::_get_available_pos(bool centered, Vector2 ofs) const while (true) { bool exists = false; - List<StringName> all_fn; - script->get_function_list(&all_fn); - for (List<StringName>::Element *F = all_fn.front(); F; F = F->next()) { - StringName curr_fn = F->get(); - List<int> existing; - script->get_node_list(curr_fn, &existing); - for (List<int>::Element *E = existing.front(); E; E = E->next()) { - Point2 pos = script->get_node_position(curr_fn, E->get()); - if (pos.distance_to(ofs) < 50) { - ofs += Vector2(graph->get_snap(), graph->get_snap()); - exists = true; - break; - } + List<int> existing; + script->get_node_list(&existing); + for (List<int>::Element *E = existing.front(); E; E = E->next()) { + Point2 pos = script->get_node_position(E->get()); + if (pos.distance_to(ofs) < 50) { + ofs += Vector2(graph->get_snap(), graph->get_snap()); + exists = true; + break; } } if (exists) { @@ -1710,7 +1683,7 @@ String VisualScriptEditor::_validate_name(const String &p_name) const { } void VisualScriptEditor::_on_nodes_delete() { - // delete all the selected nodes + // Delete all the selected nodes. List<int> to_erase; @@ -1723,7 +1696,7 @@ void VisualScriptEditor::_on_nodes_delete() { } } - if (to_erase.empty()) { + if (to_erase.is_empty()) { return; } @@ -1732,26 +1705,24 @@ void VisualScriptEditor::_on_nodes_delete() { for (List<int>::Element *F = to_erase.front(); F; F = F->next()) { int cr_node = F->get(); - StringName func = _get_function_of_node(cr_node); - - undo_redo->add_do_method(script.ptr(), "remove_node", func, cr_node); - undo_redo->add_undo_method(script.ptr(), "add_node", func, cr_node, script->get_node(func, cr_node), script->get_node_position(func, cr_node)); + undo_redo->add_do_method(script.ptr(), "remove_node", cr_node); + undo_redo->add_undo_method(script.ptr(), "add_node", cr_node, script->get_node(cr_node), script->get_node_position(cr_node)); List<VisualScript::SequenceConnection> sequence_conns; - script->get_sequence_connection_list(func, &sequence_conns); + script->get_sequence_connection_list(&sequence_conns); for (List<VisualScript::SequenceConnection>::Element *E = sequence_conns.front(); E; E = E->next()) { if (E->get().from_node == cr_node || E->get().to_node == cr_node) { - undo_redo->add_undo_method(script.ptr(), "sequence_connect", func, E->get().from_node, E->get().from_output, E->get().to_node); + undo_redo->add_undo_method(script.ptr(), "sequence_connect", E->get().from_node, E->get().from_output, E->get().to_node); } } List<VisualScript::DataConnection> data_conns; - script->get_data_connection_list(func, &data_conns); + script->get_data_connection_list(&data_conns); for (List<VisualScript::DataConnection>::Element *E = data_conns.front(); E; E = E->next()) { if (E->get().from_node == F->get() || E->get().to_node == F->get()) { - undo_redo->add_undo_method(script.ptr(), "data_connect", func, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); + undo_redo->add_undo_method(script.ptr(), "data_connect", E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); } } } @@ -1763,7 +1734,6 @@ void VisualScriptEditor::_on_nodes_delete() { void VisualScriptEditor::_on_nodes_duplicate() { Set<int> to_duplicate; - List<StringName> funcs; for (int i = 0; i < graph->get_child_count(); i++) { GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i)); @@ -1771,12 +1741,11 @@ void VisualScriptEditor::_on_nodes_duplicate() { if (gn->is_selected() && gn->is_close_button_visible()) { int id = gn->get_name().operator String().to_int(); to_duplicate.insert(id); - funcs.push_back(_get_function_of_node(id)); } } } - if (to_duplicate.empty()) { + if (to_duplicate.is_empty()) { return; } @@ -1787,9 +1756,8 @@ void VisualScriptEditor::_on_nodes_duplicate() { HashMap<int, int> remap; for (Set<int>::Element *F = to_duplicate.front(); F; F = F->next()) { - // duplicate from the specific function but place it into the default func as it would lack the connections - StringName func = _get_function_of_node(F->get()); - Ref<VisualScriptNode> node = script->get_node(func, F->get()); + // Duplicate from the specific function but place it into the default func as it would lack the connections. + Ref<VisualScriptNode> node = script->get_node(F->get()); Ref<VisualScriptNode> dupe = node->duplicate(true); @@ -1797,25 +1765,23 @@ void VisualScriptEditor::_on_nodes_duplicate() { remap.set(F->get(), new_id); to_select.insert(new_id); - undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, dupe, script->get_node_position(func, F->get()) + Vector2(20, 20)); - undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id); + undo_redo->add_do_method(script.ptr(), "add_node", new_id, dupe, script->get_node_position(F->get()) + Vector2(20, 20)); + undo_redo->add_undo_method(script.ptr(), "remove_node", new_id); } - for (List<StringName>::Element *F = funcs.front(); F; F = F->next()) { - List<VisualScript::SequenceConnection> seqs; - script->get_sequence_connection_list(F->get(), &seqs); - for (List<VisualScript::SequenceConnection>::Element *E = seqs.front(); E; E = E->next()) { - if (to_duplicate.has(E->get().from_node) && to_duplicate.has(E->get().to_node)) { - undo_redo->add_do_method(script.ptr(), "sequence_connect", default_func, remap[E->get().from_node], E->get().from_output, remap[E->get().to_node]); - } + List<VisualScript::SequenceConnection> seqs; + script->get_sequence_connection_list(&seqs); + for (List<VisualScript::SequenceConnection>::Element *E = seqs.front(); E; E = E->next()) { + if (to_duplicate.has(E->get().from_node) && to_duplicate.has(E->get().to_node)) { + undo_redo->add_do_method(script.ptr(), "sequence_connect", remap[E->get().from_node], E->get().from_output, remap[E->get().to_node]); } + } - List<VisualScript::DataConnection> data; - script->get_data_connection_list(F->get(), &data); - for (List<VisualScript::DataConnection>::Element *E = data.front(); E; E = E->next()) { - if (to_duplicate.has(E->get().from_node) && to_duplicate.has(E->get().to_node)) { - undo_redo->add_do_method(script.ptr(), "data_connect", default_func, remap[E->get().from_node], E->get().from_port, remap[E->get().to_node], E->get().to_port); - } + List<VisualScript::DataConnection> data; + script->get_data_connection_list(&data); + for (List<VisualScript::DataConnection>::Element *E = data.front(); E; E = E->next()) { + if (to_duplicate.has(E->get().from_node) && to_duplicate.has(E->get().to_node)) { + undo_redo->add_do_method(script.ptr(), "data_connect", remap[E->get().from_node], E->get().from_port, remap[E->get().to_node], E->get().to_port); } } @@ -1833,7 +1799,7 @@ void VisualScriptEditor::_on_nodes_duplicate() { } if (to_select.size()) { - EditorNode::get_singleton()->push_item(script->get_node(default_func, to_select.front()->get()).ptr()); + EditorNode::get_singleton()->push_item(script->get_node(to_select.front()->get()).ptr()); } } @@ -1846,7 +1812,7 @@ void VisualScriptEditor::_generic_search(String p_base_type, Vector2 pos, bool n new_connect_node_select->select_from_visual_script(p_base_type, false, false); // neither connecting nor reset text - // ensure that the dialog fits inside the graph + // Ensure that the dialog fits inside the graph. Size2 bounds = graph->get_global_position() + graph->get_size() - new_connect_node_select->get_size(); pos.x = pos.x > bounds.x ? bounds.x : pos.x; pos.y = pos.y > bounds.y ? bounds.y : pos.y; @@ -1907,7 +1873,7 @@ void VisualScriptEditor::_members_gui_input(const Ref<InputEvent> &p_event) { if (btn.is_valid() && btn->is_doubleclick()) { TreeItem *ti = members->get_selected(); if (ti && ti->get_parent() == members->get_root()->get_children()) { // to check if it's a function - _center_on_node(ti->get_metadata(0), script->get_function_node_id(ti->get_metadata(0))); + _center_on_node(script->get_function_node_id(ti->get_metadata(0))); } } } @@ -1925,8 +1891,8 @@ void VisualScriptEditor::_rename_function(const String &name, const String &new_ int node_id = script->get_function_node_id(name); Ref<VisualScriptFunction> func; - if (script->has_node(name, node_id)) { - func = script->get_node(name, node_id); + if (script->has_node(node_id)) { + func = script->get_node(node_id); } undo_redo->create_action(TTR("Rename Function")); undo_redo->add_do_method(script.ptr(), "rename_function", name, new_name); @@ -1936,21 +1902,17 @@ void VisualScriptEditor::_rename_function(const String &name, const String &new_ undo_redo->add_undo_method(func.ptr(), "set_name", name); } - // also fix all function calls - List<StringName> flst; - script->get_function_list(&flst); - for (List<StringName>::Element *E = flst.front(); E; E = E->next()) { - List<int> lst; - script->get_node_list(E->get(), &lst); - for (List<int>::Element *F = lst.front(); F; F = F->next()) { - Ref<VisualScriptFunctionCall> fncall = script->get_node(E->get(), F->get()); - if (!fncall.is_valid()) { - continue; - } - if (fncall->get_function() == name) { - undo_redo->add_do_method(fncall.ptr(), "set_function", new_name); - undo_redo->add_undo_method(fncall.ptr(), "set_function", name); - } + // Also fix all function calls. + List<int> lst; + script->get_node_list(&lst); + for (List<int>::Element *F = lst.front(); F; F = F->next()) { + Ref<VisualScriptFunctionCall> fncall = script->get_node(F->get()); + if (!fncall.is_valid()) { + continue; + } + if (fncall->get_function() == name) { + undo_redo->add_do_method(fncall.ptr(), "set_function", new_name); + undo_redo->add_undo_method(fncall.ptr(), "set_function", name); } } @@ -2103,7 +2065,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da ofs /= EDSCALE; - int new_id = _create_new_node_from_name(d["node_type"], ofs, default_func); + int new_id = _create_new_node_from_name(d["node_type"], ofs); Node *node = graph->get_node(itos(new_id)); if (node) { @@ -2142,8 +2104,8 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da int new_id = script->get_available_id(); undo_redo->create_action(TTR("Add Node")); - undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, vnode, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id); + undo_redo->add_do_method(script.ptr(), "add_node", new_id, vnode, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", new_id); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); @@ -2171,11 +2133,11 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da int new_id = script->get_available_id(); undo_redo->create_action(TTR("Add Node")); - undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, vnode, ofs); + undo_redo->add_do_method(script.ptr(), "add_node", new_id, vnode, ofs); undo_redo->add_do_method(vnode.ptr(), "set_base_type", script->get_instance_base_type()); undo_redo->add_do_method(vnode.ptr(), "set_function", d["function"]); - undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id); + undo_redo->add_undo_method(script.ptr(), "remove_node", new_id); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); @@ -2203,8 +2165,8 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da int new_id = script->get_available_id(); undo_redo->create_action(TTR("Add Node")); - undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, vnode, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id); + undo_redo->add_do_method(script.ptr(), "add_node", new_id, vnode, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", new_id); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); @@ -2232,8 +2194,8 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da int new_id = script->get_available_id(); undo_redo->create_action(TTR("Add Preload Node")); - undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, prnode, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id); + undo_redo->add_do_method(script.ptr(), "add_node", new_id, prnode, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", new_id); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); @@ -2272,8 +2234,8 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da prnode.instance(); prnode->set_preload(res); - undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, prnode, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id); + undo_redo->add_do_method(script.ptr(), "add_node", new_id, prnode, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", new_id); new_ids.push_back(new_id); new_id++; ofs += Vector2(20, 20) * EDSCALE; @@ -2339,7 +2301,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da scene_node->set_node_path(sn->get_path_to(node)); n = scene_node; } else { - // ! Doesn't work properly + // ! Doesn't work properly. Ref<VisualScriptFunctionCall> call; call.instance(); call->set_call_mode(VisualScriptFunctionCall::CALL_MODE_NODE_PATH); @@ -2350,8 +2312,8 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da selecting_method_id = base_id; } - undo_redo->add_do_method(script.ptr(), "add_node", default_func, base_id, n, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, base_id); + undo_redo->add_do_method(script.ptr(), "add_node", base_id, n, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", base_id); base_id++; ofs += Vector2(25, 25); @@ -2420,13 +2382,13 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da vnode = pget; } - undo_redo->add_do_method(script.ptr(), "add_node", default_func, base_id, vnode, ofs); + undo_redo->add_do_method(script.ptr(), "add_node", base_id, vnode, ofs); undo_redo->add_do_method(vnode.ptr(), "set_property", d["property"]); if (!use_get) { undo_redo->add_do_method(vnode.ptr(), "set_default_input_value", 0, d["value"]); } - undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, base_id); + undo_redo->add_undo_method(script.ptr(), "remove_node", base_id); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); @@ -2465,12 +2427,12 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da } vnode = pget; } - undo_redo->add_do_method(script.ptr(), "add_node", default_func, base_id, vnode, ofs); + undo_redo->add_do_method(script.ptr(), "add_node", base_id, vnode, ofs); undo_redo->add_do_method(vnode.ptr(), "set_property", d["property"]); if (!use_get) { undo_redo->add_do_method(vnode.ptr(), "set_default_input_value", 0, d["value"]); } - undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, base_id); + undo_redo->add_undo_method(script.ptr(), "remove_node", base_id); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); @@ -2480,7 +2442,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da } void VisualScriptEditor::_selected_method(const String &p_method, const String &p_type, const bool p_connecting) { - Ref<VisualScriptFunctionCall> vsfc = script->get_node(default_func, selecting_method_id); + Ref<VisualScriptFunctionCall> vsfc = script->get_node(selecting_method_id); if (!vsfc.is_valid()) { return; } @@ -2538,16 +2500,8 @@ void VisualScriptEditor::set_edited_resource(const RES &p_res) { script->connect("node_ports_changed", callable_mp(this, &VisualScriptEditor::_node_ports_changed)); - default_func = script->get_default_func(); - - if (!script->has_function(default_func)) // this is the supposed default function - { - script->add_function(default_func); - script->set_edited(true); //so that if a function was added it's saved - } - _update_graph(); - _update_members(); + call_deferred("_update_members"); } void VisualScriptEditor::enable_editor() { @@ -2566,7 +2520,7 @@ String VisualScriptEditor::get_name() { if (script->get_path().find("local://") == -1 && script->get_path().find("::") == -1) { name = script->get_path().get_file(); if (is_unsaved()) { - if (script->get_path().empty()) { + if (script->get_path().is_empty()) { name = TTR("[unsaved]"); } name += "(*)"; @@ -2588,13 +2542,12 @@ bool VisualScriptEditor::is_unsaved() { bool unsaved = script->is_edited() || script->are_subnodes_edited() || - script->get_path().empty(); // In memory. + script->get_path().is_empty(); // In memory. return unsaved; } Variant VisualScriptEditor::get_edit_state() { Dictionary d; - d["function"] = default_func; d["scroll"] = graph->get_scroll_ofs(); d["zoom"] = graph->get_zoom(); d["using_snap"] = graph->is_using_snap(); @@ -2604,9 +2557,6 @@ Variant VisualScriptEditor::get_edit_state() { void VisualScriptEditor::set_edit_state(const Variant &p_state) { Dictionary d = p_state; - if (d.has("function")) { - selected = default_func; - } _update_graph(); _update_members(); @@ -2625,11 +2575,11 @@ void VisualScriptEditor::set_edit_state(const Variant &p_state) { } } -void VisualScriptEditor::_center_on_node(const StringName &p_func, int p_id) { +void VisualScriptEditor::_center_on_node(int p_id) { Node *n = graph->get_node(itos(p_id)); GraphNode *gn = Object::cast_to<GraphNode>(n); - // clear selection + // Clear selection. for (int i = 0; i < graph->get_child_count(); i++) { GraphNode *gnd = Object::cast_to<GraphNode>(graph->get_child(i)); if (gnd) { @@ -2639,15 +2589,15 @@ void VisualScriptEditor::_center_on_node(const StringName &p_func, int p_id) { if (gn) { gn->set_selected(true); - Vector2 new_scroll = gn->get_offset() - graph->get_size() * 0.5 + gn->get_size() * 0.5; + Vector2 new_scroll = gn->get_position_offset() - graph->get_size() * 0.5 + gn->get_size() * 0.5; graph->set_scroll_ofs(new_scroll); - script->set_function_scroll(p_func, new_scroll / EDSCALE); + script->set_scroll(new_scroll / EDSCALE); script->set_edited(true); } } void VisualScriptEditor::goto_line(int p_line, bool p_with_error) { - p_line += 1; //add one because script lines begin from 0. + p_line += 1; // Add one because script lines begin from 0. if (p_with_error) { error_line = p_line; @@ -2656,7 +2606,7 @@ void VisualScriptEditor::goto_line(int p_line, bool p_with_error) { List<StringName> functions; script->get_function_list(&functions); for (List<StringName>::Element *E = functions.front(); E; E = E->next()) { - if (script->has_node(E->get(), p_line)) { + if (script->has_node(p_line)) { _update_graph(); _update_members(); @@ -2703,11 +2653,11 @@ Array VisualScriptEditor::get_breakpoints() { script->get_function_list(&functions); for (List<StringName>::Element *E = functions.front(); E; E = E->next()) { List<int> nodes; - script->get_node_list(E->get(), &nodes); + script->get_node_list(&nodes); for (List<int>::Element *F = nodes.front(); F; F = F->next()) { - Ref<VisualScriptNode> vsn = script->get_node(E->get(), F->get()); + Ref<VisualScriptNode> vsn = script->get_node(F->get()); if (vsn->is_breakpoint()) { - breakpoints.push_back(F->get() - 1); //subtract 1 because breakpoints in text start from zero + breakpoints.push_back(F->get() - 1); // Subtract 1 because breakpoints in text start from zero. } } } @@ -2718,7 +2668,7 @@ void VisualScriptEditor::add_callback(const String &p_function, PackedStringArra if (script->has_function(p_function)) { _update_members(); _update_graph(); - _center_on_node(p_function, script->get_function_node_id(p_function)); + _center_on_node(script->get_function_node_id(p_function)); return; } @@ -2742,15 +2692,15 @@ void VisualScriptEditor::add_callback(const String &p_function, PackedStringArra func->add_argument(type, name); } - + int fn_id = script->get_available_id(); func->set_name(p_function); - script->add_function(p_function); - script->add_node(p_function, script->get_available_id(), func); + script->add_function(p_function, fn_id); + script->add_node(fn_id, func); _update_members(); _update_graph(); - _center_on_node(p_function, script->get_function_node_id(p_function)); + _center_on_node(script->get_function_node_id(p_function)); } bool VisualScriptEditor::show_members_overview() { @@ -2844,62 +2794,46 @@ void VisualScriptEditor::_end_node_move() { undo_redo->commit_action(); } -void VisualScriptEditor::_move_node(const StringName &p_func, int p_id, const Vector2 &p_to) { - if (!script->has_function(p_func)) { +void VisualScriptEditor::_move_node(int p_id, const Vector2 &p_to) { + if (!script->has_node(p_id)) { return; } Node *node = graph->get_node(itos(p_id)); if (Object::cast_to<GraphNode>(node)) { - Object::cast_to<GraphNode>(node)->set_offset(p_to); + Object::cast_to<GraphNode>(node)->set_position_offset(p_to); } - script->set_node_position(p_func, p_id, p_to / EDSCALE); -} - -StringName VisualScriptEditor::_get_function_of_node(int p_id) const { - List<StringName> funcs; - script->get_function_list(&funcs); - for (List<StringName>::Element *E = funcs.front(); E; E = E->next()) { - if (script->has_node(E->get(), p_id)) { - return E->get(); - } - } - - return ""; // this is passed to avoid crash and is tested against later + script->set_node_position(p_id, p_to / EDSCALE); } void VisualScriptEditor::_node_moved(Vector2 p_from, Vector2 p_to, int p_id) { - StringName func = _get_function_of_node(p_id); - - undo_redo->add_do_method(this, "_move_node", func, p_id, p_to); - undo_redo->add_undo_method(this, "_move_node", func, p_id, p_from); + undo_redo->add_do_method(this, "_move_node", p_id, p_to); + undo_redo->add_undo_method(this, "_move_node", p_id, p_from); } void VisualScriptEditor::_remove_node(int p_id) { undo_redo->create_action(TTR("Remove VisualScript Node")); - StringName func = _get_function_of_node(p_id); - - undo_redo->add_do_method(script.ptr(), "remove_node", func, p_id); - undo_redo->add_undo_method(script.ptr(), "add_node", func, p_id, script->get_node(func, p_id), script->get_node_position(func, p_id)); + undo_redo->add_do_method(script.ptr(), "remove_node", p_id); + undo_redo->add_undo_method(script.ptr(), "add_node", p_id, script->get_node(p_id), script->get_node_position(p_id)); List<VisualScript::SequenceConnection> sequence_conns; - script->get_sequence_connection_list(func, &sequence_conns); + script->get_sequence_connection_list(&sequence_conns); for (List<VisualScript::SequenceConnection>::Element *E = sequence_conns.front(); E; E = E->next()) { if (E->get().from_node == p_id || E->get().to_node == p_id) { - undo_redo->add_undo_method(script.ptr(), "sequence_connect", func, E->get().from_node, E->get().from_output, E->get().to_node); + undo_redo->add_undo_method(script.ptr(), "sequence_connect", E->get().from_node, E->get().from_output, E->get().to_node); } } List<VisualScript::DataConnection> data_conns; - script->get_data_connection_list(func, &data_conns); + script->get_data_connection_list(&data_conns); for (List<VisualScript::DataConnection>::Element *E = data_conns.front(); E; E = E->next()) { if (E->get().from_node == p_id || E->get().to_node == p_id) { - undo_redo->add_undo_method(script.ptr(), "data_connect", func, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); + undo_redo->add_undo_method(script.ptr(), "data_connect", E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); } } @@ -2909,13 +2843,13 @@ void VisualScriptEditor::_remove_node(int p_id) { undo_redo->commit_action(); } -void VisualScriptEditor::_node_ports_changed(const String &p_func, int p_id) { +void VisualScriptEditor::_node_ports_changed(int p_id) { _update_graph(p_id); } -bool VisualScriptEditor::node_has_sequence_connections(const StringName &p_func, int p_id) { +bool VisualScriptEditor::node_has_sequence_connections(int p_id) { List<VisualScript::SequenceConnection> sequence_conns; - script->get_sequence_connection_list(p_func, &sequence_conns); + script->get_sequence_connection_list(&sequence_conns); for (List<VisualScript::SequenceConnection>::Element *E = sequence_conns.front(); E; E = E->next()) { int from = E->get().from_node; @@ -2930,9 +2864,7 @@ bool VisualScriptEditor::node_has_sequence_connections(const StringName &p_func, } void VisualScriptEditor::_graph_connected(const String &p_from, int p_from_slot, const String &p_to, int p_to_slot) { - StringName from_func = _get_function_of_node(p_from.to_int()); - - Ref<VisualScriptNode> from_node = script->get_node(from_func, p_from.to_int()); + Ref<VisualScriptNode> from_node = script->get_node(p_from.to_int()); ERR_FAIL_COND(!from_node.is_valid()); bool from_seq; @@ -2942,9 +2874,7 @@ void VisualScriptEditor::_graph_connected(const String &p_from, int p_from_slot, return; //can't connect this, it's invalid } - StringName to_func = _get_function_of_node(p_to.to_int()); - - Ref<VisualScriptNode> to_node = script->get_node(to_func, p_to.to_int()); + Ref<VisualScriptNode> to_node = script->get_node(p_to.to_int()); ERR_FAIL_COND(!to_node.is_valid()); bool to_seq; @@ -2956,56 +2886,23 @@ void VisualScriptEditor::_graph_connected(const String &p_from, int p_from_slot, ERR_FAIL_COND(from_seq != to_seq); - // Do all the checks here - StringName func; // this the func where we store the one the nodes at the end of the resolution on having multiple nodes + // Do all the checks here. + StringName func; // This the func where we store the one the nodes at the end of the resolution on having multiple nodes. undo_redo->create_action(TTR("Connect Nodes")); - if (from_func == to_func) { - func = to_func; - } else if (from_seq) { - // this is a sequence connection - _move_nodes_with_rescan(to_func, from_func, p_to.to_int()); // this function moves the nodes from func1 to func2 - func = from_func; - } else { - if (node_has_sequence_connections(to_func, p_to.to_int())) { - if (node_has_sequence_connections(from_func, p_from.to_int())) { - ERR_PRINT("Trying to connect between different sequence node trees"); - return; - } else { - _move_nodes_with_rescan(from_func, to_func, p_from.to_int()); - func = to_func; - } - } else if (node_has_sequence_connections(from_func, p_from.to_int())) { - if (from_func == default_func) { - _move_nodes_with_rescan(from_func, to_func, p_from.to_int()); - func = to_func; - } else { - _move_nodes_with_rescan(to_func, from_func, p_to.to_int()); - func = from_func; - } - } else { - if (to_func == default_func) { - _move_nodes_with_rescan(to_func, from_func, p_to.to_int()); - func = from_func; - } else { - _move_nodes_with_rescan(from_func, to_func, p_from.to_int()); - func = to_func; - } - } - } - if (from_seq) { - undo_redo->add_do_method(script.ptr(), "sequence_connect", func, p_from.to_int(), from_port, p_to.to_int()); - // this undo error on undo after move can't be removed without painful gymnastics - undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", func, p_from.to_int(), from_port, p_to.to_int()); + undo_redo->add_do_method(script.ptr(), "sequence_connect", p_from.to_int(), from_port, p_to.to_int()); + // This undo error on undo after move can't be removed without painful gymnastics + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", p_from.to_int(), from_port, p_to.to_int()); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); } else { bool converted = false; - int conv_node = -1; Ref<VisualScriptOperator> oper = to_node; if (oper.is_valid() && oper->get_typed() == Variant::NIL) { - // it's an operator Node and if the type is already nil + // It's an operator Node and if the type is already nil if (from_node->get_output_value_port_info(from_port).type != Variant::NIL) { oper->set_typed(from_node->get_output_value_port_info(from_port).type); } @@ -3013,106 +2910,36 @@ void VisualScriptEditor::_graph_connected(const String &p_from, int p_from_slot, Ref<VisualScriptOperator> operf = from_node; if (operf.is_valid() && operf->get_typed() == Variant::NIL) { - // it's an operator Node and if the type is already nil + // It's an operator Node and if the type is already nil if (to_node->get_input_value_port_info(to_port).type != Variant::NIL) { operf->set_typed(to_node->get_input_value_port_info(to_port).type); } } - Variant::Type to_type = to_node->get_input_value_port_info(to_port).type; - Variant::Type from_type = from_node->get_output_value_port_info(from_port).type; - - if (to_type != Variant::NIL && from_type != Variant::NIL && to_type != from_type) { - // add a constructor node between the ports - bool exceptions = false; // true if there are any exceptions - exceptions = exceptions || (to_type == Variant::INT && from_type == Variant::FLOAT); - exceptions = exceptions || (to_type == Variant::FLOAT && from_type == Variant::INT); - if (Variant::can_convert(from_type, to_type) && !exceptions) { - MethodInfo mi; - mi.name = Variant::get_type_name(to_type); - PropertyInfo pi; - pi.name = "from"; - pi.type = from_type; - mi.arguments.push_back(pi); - mi.return_val.type = to_type; - // we know that this is allowed so create a new constructor node - Ref<VisualScriptConstructor> constructor; - constructor.instance(); - constructor->set_constructor_type(to_type); - constructor->set_constructor(mi); - // add the new constructor node - - GraphNode *gn = Object::cast_to<GraphNode>(graph->get_node(p_from)); - GraphNode *gn2 = Object::cast_to<GraphNode>(graph->get_node(p_to)); - if (gn && gn2) { - Vector2 from_node_size = gn->get_rect().get_size(); - Vector2 to_node_size = gn2->get_rect().get_size(); - Vector2 to_node_pos = script->get_node_position(func, p_to.to_int()); - Vector2 from_node_pos = script->get_node_position(func, p_from.to_int()); - Vector2 new_to_node_pos = from_node_pos; - Vector2 constructor_pos; - if ((to_node_pos.x - from_node_pos.x) < 0) { - // to is behind from node - if (to_node_pos.x > (from_node_pos.x - to_node_size.x - 240)) { - new_to_node_pos.x = from_node_pos.x - to_node_size.x - 240; // approx size of constructor node + padding - } else { - new_to_node_pos.x = to_node_pos.x; - } - new_to_node_pos.y = to_node_pos.y; - constructor_pos.x = from_node_pos.x - 210; - constructor_pos.y = to_node_pos.y; - } else { - // to is ahead of from node - if (to_node_pos.x < (from_node_size.x + from_node_pos.x + 240)) { - new_to_node_pos.x = from_node_size.x + from_node_pos.x + 240; // approx size of constructor node + padding - } else { - new_to_node_pos.x = to_node_pos.x; - } - new_to_node_pos.y = to_node_pos.y; - constructor_pos.x = from_node_size.x + from_node_pos.x + 10; - constructor_pos.y = to_node_pos.y; - } - undo_redo->add_do_method(this, "_move_node", func, p_to.to_int(), new_to_node_pos); - undo_redo->add_undo_method(this, "_move_node", func, p_to.to_int(), to_node_pos); - conv_node = script->get_available_id(); - undo_redo->add_do_method(script.ptr(), "add_node", func, conv_node, constructor, _get_available_pos(false, constructor_pos)); - undo_redo->add_undo_method(script.ptr(), "remove_node", func, conv_node); - converted = true; - } - } - } - - // disconnect current, and connect the new one - if (script->is_input_value_port_connected(func, p_to.to_int(), to_port)) { + // Disconnect current, and connect the new one + if (script->is_input_value_port_connected(p_to.to_int(), to_port)) { if (can_swap && data_disconnect_node == p_to.to_int()) { int conn_from; int conn_port; - script->get_input_value_port_connection_source(func, p_to.to_int(), to_port, &conn_from, &conn_port); - undo_redo->add_do_method(script.ptr(), "data_disconnect", func, conn_from, conn_port, p_to.to_int(), to_port); - undo_redo->add_do_method(script.ptr(), "data_connect", func, conn_from, conn_port, data_disconnect_node, data_disconnect_port); - undo_redo->add_undo_method(script.ptr(), "data_disconnect", func, conn_from, conn_port, data_disconnect_node, data_disconnect_port); - undo_redo->add_undo_method(script.ptr(), "data_connect", func, conn_from, conn_port, p_to.to_int(), to_port); + script->get_input_value_port_connection_source(p_to.to_int(), to_port, &conn_from, &conn_port); + undo_redo->add_do_method(script.ptr(), "data_disconnect", conn_from, conn_port, p_to.to_int(), to_port); + undo_redo->add_do_method(script.ptr(), "data_connect", conn_from, conn_port, data_disconnect_node, data_disconnect_port); + undo_redo->add_undo_method(script.ptr(), "data_disconnect", conn_from, conn_port, data_disconnect_node, data_disconnect_port); + undo_redo->add_undo_method(script.ptr(), "data_connect", conn_from, conn_port, p_to.to_int(), to_port); can_swap = false; // swapped } else { int conn_from; int conn_port; - script->get_input_value_port_connection_source(func, p_to.to_int(), to_port, &conn_from, &conn_port); - undo_redo->add_do_method(script.ptr(), "data_disconnect", func, conn_from, conn_port, p_to.to_int(), to_port); - undo_redo->add_undo_method(script.ptr(), "data_connect", func, conn_from, conn_port, p_to.to_int(), to_port); + script->get_input_value_port_connection_source(p_to.to_int(), to_port, &conn_from, &conn_port); + undo_redo->add_do_method(script.ptr(), "data_disconnect", conn_from, conn_port, p_to.to_int(), to_port); + undo_redo->add_undo_method(script.ptr(), "data_connect", conn_from, conn_port, p_to.to_int(), to_port); } } if (!converted) { - undo_redo->add_do_method(script.ptr(), "data_connect", func, p_from.to_int(), from_port, p_to.to_int(), to_port); - undo_redo->add_undo_method(script.ptr(), "data_disconnect", func, p_from.to_int(), from_port, p_to.to_int(), to_port); - } else { - // this is noice - undo_redo->add_do_method(script.ptr(), "data_connect", func, p_from.to_int(), from_port, conv_node, 0); - undo_redo->add_do_method(script.ptr(), "data_connect", func, conv_node, 0, p_to.to_int(), to_port); - // I don't think this is needed but gonna leave it here for now... until I need to finalise it all - undo_redo->add_undo_method(script.ptr(), "data_disconnect", func, p_from.to_int(), from_port, conv_node, 0); - undo_redo->add_undo_method(script.ptr(), "data_disconnect", func, conv_node, 0, p_to.to_int(), to_port); + undo_redo->add_do_method(script.ptr(), "data_connect", p_from.to_int(), from_port, p_to.to_int(), to_port); + undo_redo->add_undo_method(script.ptr(), "data_disconnect", p_from.to_int(), from_port, p_to.to_int(), to_port); } - //update nodes in graph + // Update nodes in graph if (!converted) { undo_redo->add_do_method(this, "_update_graph", p_from.to_int()); undo_redo->add_do_method(this, "_update_graph", p_to.to_int()); @@ -3124,34 +2951,28 @@ void VisualScriptEditor::_graph_connected(const String &p_from, int p_from_slot, } } - undo_redo->add_do_method(this, "_update_graph_connections"); - undo_redo->add_undo_method(this, "_update_graph_connections"); - undo_redo->commit_action(); } void VisualScriptEditor::_graph_disconnected(const String &p_from, int p_from_slot, const String &p_to, int p_to_slot) { - StringName func = _get_function_of_node(p_from.to_int()); - ERR_FAIL_COND(func != _get_function_of_node(p_to.to_int())); - - Ref<VisualScriptNode> from_node = script->get_node(func, p_from.to_int()); + Ref<VisualScriptNode> from_node = script->get_node(p_from.to_int()); ERR_FAIL_COND(!from_node.is_valid()); bool from_seq; int from_port; if (!_get_out_slot(from_node, p_from_slot, from_port, from_seq)) { - return; //can't connect this, it's invalid + return; // Can't connect this, it's invalid. } - Ref<VisualScriptNode> to_node = script->get_node(func, p_to.to_int()); + Ref<VisualScriptNode> to_node = script->get_node(p_to.to_int()); ERR_FAIL_COND(!to_node.is_valid()); bool to_seq; int to_port; if (!_get_in_slot(to_node, p_to_slot, to_port, to_seq)) { - return; //can't connect this, it's invalid + return; // Can't connect this, it's invalid. } ERR_FAIL_COND(from_seq != to_seq); @@ -3159,248 +2980,27 @@ void VisualScriptEditor::_graph_disconnected(const String &p_from, int p_from_sl undo_redo->create_action(TTR("Disconnect Nodes")); if (from_seq) { - undo_redo->add_do_method(script.ptr(), "sequence_disconnect", func, p_from.to_int(), from_port, p_to.to_int()); - undo_redo->add_undo_method(script.ptr(), "sequence_connect", func, p_from.to_int(), from_port, p_to.to_int()); + undo_redo->add_do_method(script.ptr(), "sequence_disconnect", p_from.to_int(), from_port, p_to.to_int()); + undo_redo->add_undo_method(script.ptr(), "sequence_connect", p_from.to_int(), from_port, p_to.to_int()); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); } else { can_swap = true; data_disconnect_node = p_to.to_int(); data_disconnect_port = to_port; - undo_redo->add_do_method(script.ptr(), "data_disconnect", func, p_from.to_int(), from_port, p_to.to_int(), to_port); - undo_redo->add_undo_method(script.ptr(), "data_connect", func, p_from.to_int(), from_port, p_to.to_int(), to_port); - //update relevant nodes in the graph + undo_redo->add_do_method(script.ptr(), "data_disconnect", p_from.to_int(), from_port, p_to.to_int(), to_port); + undo_redo->add_undo_method(script.ptr(), "data_connect", p_from.to_int(), from_port, p_to.to_int(), to_port); + // Update relevant nodes in the graph. undo_redo->add_do_method(this, "_update_graph", p_from.to_int()); undo_redo->add_do_method(this, "_update_graph", p_to.to_int()); undo_redo->add_undo_method(this, "_update_graph", p_from.to_int()); undo_redo->add_undo_method(this, "_update_graph", p_to.to_int()); } - undo_redo->add_do_method(this, "_update_graph_connections"); - undo_redo->add_undo_method(this, "_update_graph_connections"); undo_redo->commit_action(); } -void VisualScriptEditor::_move_nodes_with_rescan(const StringName &p_func_from, const StringName &p_func_to, int p_id) { - Set<int> nodes_to_move; - HashMap<int, Map<int, int>> seqconns_to_move; // from => List(outp, to) - HashMap<int, Map<int, Pair<int, int>>> dataconns_to_move; // to => List(inp_p => from, outp) - - nodes_to_move.insert(p_id); - Set<int> sequence_connections; - { - List<VisualScript::SequenceConnection> sequence_conns; - script->get_sequence_connection_list(p_func_from, &sequence_conns); - - HashMap<int, Map<int, int>> seqcons; // from => List(out_p => to) - - for (List<VisualScript::SequenceConnection>::Element *E = sequence_conns.front(); E; E = E->next()) { - int from = E->get().from_node; - int to = E->get().to_node; - int out_p = E->get().from_output; - if (!seqcons.has(from)) { - seqcons.set(from, Map<int, int>()); - } - seqcons[from].insert(out_p, to); - sequence_connections.insert(to); - sequence_connections.insert(from); - } - - int conn = p_id; - List<int> stack; - HashMap<int, Set<int>> seen; // from, outp - while (seqcons.has(conn)) { - for (auto E = seqcons[conn].front(); E; E = E->next()) { - if (seen.has(conn) && seen[conn].has(E->key())) { - if (!E->next()) { - if (stack.size() > 0) { - conn = stack.back()->get(); - stack.pop_back(); - break; - } - conn = -101; - break; - } - continue; - } - if (!seen.has(conn)) { - seen.set(conn, Set<int>()); - } - seen[conn].insert(E->key()); - stack.push_back(conn); - if (!seqconns_to_move.has(conn)) { - seqconns_to_move.set(conn, Map<int, int>()); - } - seqconns_to_move[conn].insert(E->key(), E->get()); - conn = E->get(); - nodes_to_move.insert(conn); - break; - } - if (!seqcons.has(conn) && stack.size() > 0) { - conn = stack.back()->get(); - stack.pop_back(); - } - } - } - - { - List<VisualScript::DataConnection> data_connections; - script->get_data_connection_list(p_func_from, &data_connections); - int func_from_node_id = script->get_function_node_id(p_func_from); - - HashMap<int, Map<int, Pair<int, int>>> connections; - - for (List<VisualScript::DataConnection>::Element *E = data_connections.front(); E; E = E->next()) { - int from = E->get().from_node; - int to = E->get().to_node; - int out_p = E->get().from_port; - int in_p = E->get().to_port; - - // skip if the from_node is a function node - if (from == func_from_node_id) { - continue; - } - - if (!connections.has(to)) { - connections.set(to, Map<int, Pair<int, int>>()); - } - connections[to].insert(in_p, Pair<int, int>(from, out_p)); - } - - // go through the HashMap and do all sorts of crazy ass stuff now... - Set<int> nodes_to_be_added; - for (Set<int>::Element *F = nodes_to_move.front(); F; F = F->next()) { - HashMap<int, Set<int>> seen; - List<int> stack; - int id = F->get(); - while (connections.has(id)) { - for (auto E = connections[id].front(); E; E = E->next()) { - if (seen.has(id) && seen[id].has(E->key())) { - if (!E->next()) { - if (stack.size() > 0) { - id = stack.back()->get(); - stack.pop_back(); - break; - } - id = -11; // I assume ids can't be negative should confirm it... - break; - } - continue; - } - - if (sequence_connections.has(E->get().first)) { - if (!nodes_to_move.has(E->get().first)) { - if (stack.size() > 0) { - id = stack.back()->get(); - stack.pop_back(); - break; - } - id = -11; // I assume ids can't be negative should confirm it... - break; - } - } - - if (!seen.has(id)) { - seen.set(id, Set<int>()); - } - seen[id].insert(E->key()); - stack.push_back(id); - if (!dataconns_to_move.has(id)) { - dataconns_to_move.set(id, Map<int, Pair<int, int>>()); - } - dataconns_to_move[id].insert(E->key(), Pair<int, int>(E->get().first, E->get().second)); - id = E->get().first; - nodes_to_be_added.insert(id); - break; - } - if (!connections.has(id) && stack.size() > 0) { - id = stack.back()->get(); - stack.pop_back(); - } - } - } - for (Set<int>::Element *E = nodes_to_be_added.front(); E; E = E->next()) { - nodes_to_move.insert(E->get()); - } - } - - // * this is primarily for the sake of the having proper undo - List<VisualScript::SequenceConnection> seqext; - List<VisualScript::DataConnection> dataext; - - List<VisualScript::SequenceConnection> seq_connections; - script->get_sequence_connection_list(p_func_from, &seq_connections); - - for (List<VisualScript::SequenceConnection>::Element *E = seq_connections.front(); E; E = E->next()) { - if (!nodes_to_move.has(E->get().from_node) && nodes_to_move.has(E->get().to_node)) { - seqext.push_back(E->get()); - } else if (nodes_to_move.has(E->get().from_node) && !nodes_to_move.has(E->get().to_node)) { - seqext.push_back(E->get()); - } - } - - List<VisualScript::DataConnection> data_connections; - script->get_data_connection_list(p_func_from, &data_connections); - - for (List<VisualScript::DataConnection>::Element *E = data_connections.front(); E; E = E->next()) { - if (!nodes_to_move.has(E->get().from_node) && nodes_to_move.has(E->get().to_node)) { - dataext.push_back(E->get()); - } else if (nodes_to_move.has(E->get().from_node) && !nodes_to_move.has(E->get().to_node)) { - dataext.push_back(E->get()); - } - } - - // undo_redo->create_action("Rescan Functions"); - - for (Set<int>::Element *E = nodes_to_move.front(); E; E = E->next()) { - int id = E->get(); - - undo_redo->add_do_method(script.ptr(), "remove_node", p_func_from, id); - undo_redo->add_do_method(script.ptr(), "add_node", p_func_to, id, script->get_node(p_func_from, id), script->get_node_position(p_func_from, id)); - - undo_redo->add_undo_method(script.ptr(), "remove_node", p_func_to, id); - undo_redo->add_undo_method(script.ptr(), "add_node", p_func_from, id, script->get_node(p_func_from, id), script->get_node_position(p_func_from, id)); - } - - List<int> skeys; - seqconns_to_move.get_key_list(&skeys); - for (List<int>::Element *E = skeys.front(); E; E = E->next()) { - int from_node = E->get(); - for (Map<int, int>::Element *F = seqconns_to_move[from_node].front(); F; F = F->next()) { - int from_port = F->key(); - int to_node = F->get(); - undo_redo->add_do_method(script.ptr(), "sequence_connect", p_func_to, from_node, from_port, to_node); - undo_redo->add_undo_method(script.ptr(), "sequence_connect", p_func_from, from_node, from_port, to_node); - } - } - - List<int> keys; - dataconns_to_move.get_key_list(&keys); - for (List<int>::Element *E = keys.front(); E; E = E->next()) { - int to_node = E->get(); // to_node - for (Map<int, Pair<int, int>>::Element *F = dataconns_to_move[E->get()].front(); F; F = F->next()) { - int inp_p = F->key(); - Pair<int, int> fro = F->get(); - - undo_redo->add_do_method(script.ptr(), "data_connect", p_func_to, fro.first, fro.second, to_node, inp_p); - undo_redo->add_undo_method(script.ptr(), "data_connect", p_func_from, fro.first, fro.second, to_node, inp_p); - } - } - - // this to have proper undo operations - for (List<VisualScript::SequenceConnection>::Element *E = seqext.front(); E; E = E->next()) { - undo_redo->add_undo_method(script.ptr(), "sequence_connect", p_func_from, E->get().from_node, E->get().from_output, E->get().to_node); - } - for (List<VisualScript::DataConnection>::Element *E = dataext.front(); E; E = E->next()) { - undo_redo->add_undo_method(script.ptr(), "data_connect", p_func_from, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); - } - // this doesn't need do methods as they are handled by the subsequent do calls implicitly - - undo_redo->add_do_method(this, "_update_graph"); - undo_redo->add_undo_method(this, "_update_graph"); - - // undo_redo->commit_action(); -} - void VisualScriptEditor::_graph_connect_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_pos) { Node *node = graph->get_node(p_from); GraphNode *gn = Object::cast_to<GraphNode>(node); @@ -3408,23 +3008,22 @@ void VisualScriptEditor::_graph_connect_to_empty(const String &p_from, int p_fro return; } - StringName func = _get_function_of_node(p_from.to_int()); - - Ref<VisualScriptNode> vsn = script->get_node(func, p_from.to_int()); + Ref<VisualScriptNode> vsn = script->get_node(p_from.to_int()); if (!vsn.is_valid()) { return; } + if (vsn->get_output_value_port_count()) - port_action_pos = p_release_pos; + port_action_pos = p_release_pos; if (p_from_slot < vsn->get_output_sequence_port_count()) { port_action_node = p_from.to_int(); port_action_output = p_from_slot; - _port_action_menu(CREATE_ACTION, func); + _port_action_menu(CREATE_ACTION); } else { port_action_output = p_from_slot - vsn->get_output_sequence_port_count(); port_action_node = p_from.to_int(); - _port_action_menu(CREATE_CALL_SET_GET, func); + _port_action_menu(CREATE_CALL_SET_GET); } } @@ -3438,9 +3037,7 @@ VisualScriptNode::TypeGuess VisualScriptEditor::_guess_output_type(int p_port_ac visited_nodes.insert(p_port_action_node); - StringName func = _get_function_of_node(p_port_action_node); - - Ref<VisualScriptNode> node = script->get_node(func, p_port_action_node); + Ref<VisualScriptNode> node = script->get_node(p_port_action_node); if (!node.is_valid()) { return tg; @@ -3454,11 +3051,11 @@ VisualScriptNode::TypeGuess VisualScriptEditor::_guess_output_type(int p_port_ac g.type = pi.type; if (g.type == Variant::NIL || g.type == Variant::OBJECT) { - //any or object input, must further guess what this is + // Any or object input, must further guess what this is. int from_node; int from_port; - if (script->get_input_value_port_connection_source(func, p_port_action_node, i, &from_node, &from_port)) { + if (script->get_input_value_port_connection_source(p_port_action_node, i, &from_node, &from_port)) { g = _guess_output_type(from_node, from_port, visited_nodes); } else { Variant defval = node->get_default_input_value(i); @@ -3480,7 +3077,7 @@ VisualScriptNode::TypeGuess VisualScriptEditor::_guess_output_type(int p_port_ac return node->guess_output_type(in_guesses.ptrw(), p_port_action_output); } -void VisualScriptEditor::_port_action_menu(int p_option, const StringName &func) { +void VisualScriptEditor::_port_action_menu(int p_option) { Vector2 ofs = graph->get_scroll_ofs() + port_action_pos; if (graph->is_using_snap()) { int snap = graph->get_snap(); @@ -3503,8 +3100,8 @@ void VisualScriptEditor::_port_action_menu(int p_option, const StringName &func) n->set_base_type("Object"); } String type_string; - if (script->get_node(func, port_action_node)->get_output_value_port_count() > 0) { - type_string = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + if (script->get_node(port_action_node)->get_output_value_port_count() > 0) { + type_string = script->get_node(port_action_node)->get_output_value_port_info(port_action_output).hint_string; } if (tg.type == Variant::OBJECT) { if (tg.script.is_valid()) { @@ -3519,7 +3116,7 @@ void VisualScriptEditor::_port_action_menu(int p_option, const StringName &func) } else { new_connect_node_select->select_from_basic_type(tg.type); } - // ensure that the dialog fits inside the graph + // Ensure that the dialog fits inside the graph. Vector2 pos = mouse_up_position; Size2 bounds = graph->get_global_position() + graph->get_size() - new_connect_node_select->get_size(); pos.x = pos.x > bounds.x ? bounds.x : pos.x; @@ -3529,8 +3126,8 @@ void VisualScriptEditor::_port_action_menu(int p_option, const StringName &func) case CREATE_ACTION: { VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn); PropertyInfo property_info; - if (script->get_node(func, port_action_node)->get_output_value_port_count() > 0) { - property_info = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output); + if (script->get_node(port_action_node)->get_output_value_port_count() > 0) { + property_info = script->get_node(port_action_node)->get_output_value_port_info(port_action_output); } if (tg.type == Variant::OBJECT) { if (property_info.type == Variant::OBJECT && property_info.hint_string != String()) { @@ -3543,7 +3140,7 @@ void VisualScriptEditor::_port_action_menu(int p_option, const StringName &func) } else { new_connect_node_select->select_from_action(Variant::get_type_name(tg.type)); } - // ensure that the dialog fits inside the graph + // Ensure that the dialog fits inside the graph. Vector2 pos = mouse_up_position; Size2 bounds = graph->get_global_position() + graph->get_size() - new_connect_node_select->get_size(); pos.x = pos.x > bounds.x ? bounds.x : pos.x; @@ -3572,9 +3169,8 @@ void VisualScriptEditor::connect_data(Ref<VisualScriptNode> vnode_old, Ref<Visua if (port >= value_count) { port = 0; } - StringName func = _get_function_of_node(port_action_node); - undo_redo->add_do_method(script.ptr(), "data_connect", func, port_action_node, port, new_id, 0); - undo_redo->add_undo_method(script.ptr(), "data_disconnect", func, port_action_node, port, new_id, 0); + undo_redo->add_do_method(script.ptr(), "data_connect", port_action_node, port, new_id, 0); + undo_redo->add_undo_method(script.ptr(), "data_disconnect", port_action_node, port, new_id, 0); undo_redo->commit_action(); } @@ -3591,17 +3187,16 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri bool port_node_exists = true; - StringName func = _get_function_of_node(port_action_node); - if (func == StringName()) { - func = default_func; - port_node_exists = false; - } + // if (func == StringName()) { + // func = default_func; + // port_node_exists = false; + // } if (p_category == "visualscript") { Ref<VisualScriptNode> vnode_new = VisualScriptLanguage::singleton->create_node_from_name(p_text); Ref<VisualScriptNode> vnode_old; - if (port_node_exists) { - vnode_old = script->get_node(func, port_action_node); + if (port_node_exists && p_connecting) { + vnode_old = script->get_node(port_action_node); } int new_id = script->get_available_id(); @@ -3624,13 +3219,13 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri } undo_redo->create_action(TTR("Add Node")); - undo_redo->add_do_method(script.ptr(), "add_node", func, new_id, vnode_new, ofs); + undo_redo->add_do_method(script.ptr(), "add_node", new_id, vnode_new, ofs); if (vnode_old.is_valid() && p_connecting) { connect_seq(vnode_old, vnode_new, new_id); connect_data(vnode_old, vnode_new, new_id); } - undo_redo->add_undo_method(script.ptr(), "remove_node", func, new_id); + undo_redo->add_undo_method(script.ptr(), "remove_node", new_id); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); @@ -3687,8 +3282,8 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri int new_id = script->get_available_id(); undo_redo->create_action(TTR("Add Node")); - undo_redo->add_do_method(script.ptr(), "add_node", func, new_id, vnode, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_node", func, new_id); + undo_redo->add_do_method(script.ptr(), "add_node", new_id, vnode, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", new_id); undo_redo->add_do_method(this, "_update_graph", new_id); undo_redo->add_undo_method(this, "_update_graph", new_id); undo_redo->commit_action(); @@ -3699,7 +3294,7 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri port_action_new_node = new_id; - Ref<VisualScriptNode> vsn = script->get_node(func, port_action_new_node); + Ref<VisualScriptNode> vsn = script->get_node(port_action_new_node); if (Object::cast_to<VisualScriptFunctionCall>(vsn.ptr())) { Ref<VisualScriptFunctionCall> vsfc = vsn; @@ -3713,10 +3308,9 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri vsfc->set_base_type(String("")); if (tg.gdclass != StringName()) { vsfc->set_base_type(tg.gdclass); - - } else if (script->get_node(func, port_action_node).is_valid()) { - PropertyHint hint = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint; - String base_type = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + } else if (script->get_node(port_action_node).is_valid()) { + PropertyHint hint = script->get_node(port_action_node)->get_output_value_port_info(port_action_output).hint; + String base_type = script->get_node(port_action_node)->get_output_value_port_info(port_action_output).hint_string; if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) { vsfc->set_base_type(base_type); @@ -3749,9 +3343,9 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri if (tg.gdclass != StringName()) { vsp->set_base_type(tg.gdclass); - } else if (script->get_node(func, port_action_node).is_valid()) { - PropertyHint hint = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint; - String base_type = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + } else if (script->get_node(port_action_node).is_valid()) { + PropertyHint hint = script->get_node(port_action_node)->get_output_value_port_info(port_action_output).hint; + String base_type = script->get_node(port_action_node)->get_output_value_port_info(port_action_output).hint_string; if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) { vsp->set_base_type(base_type); @@ -3779,9 +3373,9 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri if (tg.gdclass != StringName()) { vsp->set_base_type(tg.gdclass); - } else if (script->get_node(func, port_action_node).is_valid()) { - PropertyHint hint = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint; - String base_type = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + } else if (script->get_node(port_action_node).is_valid()) { + PropertyHint hint = script->get_node(port_action_node)->get_output_value_port_info(port_action_output).hint; + String base_type = script->get_node(port_action_node)->get_output_value_port_info(port_action_output).hint_string; if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) { vsp->set_base_type(base_type); } @@ -3799,16 +3393,13 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri } } if (port_node_exists) { - Ref<VisualScriptNode> vnode_old = script->get_node(func, port_action_node); + Ref<VisualScriptNode> vnode_old = script->get_node(port_action_node); if (vnode_old.is_valid() && p_connecting) { connect_seq(vnode_old, vnode, port_action_new_node); connect_data(vnode_old, vnode, port_action_new_node); } } _update_graph(port_action_new_node); - if (port_node_exists) { - _update_graph_connections(); - } } void VisualScriptEditor::connect_seq(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode_new, int new_id) { @@ -3827,29 +3418,27 @@ void VisualScriptEditor::connect_seq(Ref<VisualScriptNode> vnode_old, Ref<Visual return; } - StringName func = _get_function_of_node(port_action_node); - undo_redo->create_action(TTR("Connect Node Sequence")); int pass_port = -vnode_old->get_output_sequence_port_count() + 1; int return_port = port_action_output - 1; if (vnode_old->get_output_value_port_info(port_action_output).name == String("pass") && - !script->get_output_sequence_ports_connected(func, port_action_node).has(pass_port)) { - undo_redo->add_do_method(script.ptr(), "sequence_connect", func, port_action_node, pass_port, new_id); - undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", func, port_action_node, pass_port, new_id); + !script->get_output_sequence_ports_connected(port_action_node).has(pass_port)) { + undo_redo->add_do_method(script.ptr(), "sequence_connect", port_action_node, pass_port, new_id); + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", port_action_node, pass_port, new_id); } else if (vnode_old->get_output_value_port_info(port_action_output).name == String("return") && - !script->get_output_sequence_ports_connected(func, port_action_node).has(return_port)) { - undo_redo->add_do_method(script.ptr(), "sequence_connect", func, port_action_node, return_port, new_id); - undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", func, port_action_node, return_port, new_id); + !script->get_output_sequence_ports_connected(port_action_node).has(return_port)) { + undo_redo->add_do_method(script.ptr(), "sequence_connect", port_action_node, return_port, new_id); + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", port_action_node, return_port, new_id); } else { for (int port = 0; port < vnode_old->get_output_sequence_port_count(); port++) { int count = vnode_old->get_output_sequence_port_count(); - if (port_action_output < count && !script->get_output_sequence_ports_connected(func, port_action_node).has(port_action_output)) { - undo_redo->add_do_method(script.ptr(), "sequence_connect", func, port_action_node, port_action_output, new_id); - undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", func, port_action_node, port_action_output, new_id); + if (port_action_output < count && !script->get_output_sequence_ports_connected(port_action_node).has(port_action_output)) { + undo_redo->add_do_method(script.ptr(), "sequence_connect", port_action_node, port_action_output, new_id); + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", port_action_node, port_action_output, new_id); break; - } else if (!script->get_output_sequence_ports_connected(func, port_action_node).has(port)) { - undo_redo->add_do_method(script.ptr(), "sequence_connect", func, port_action_node, port, new_id); - undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", func, port_action_node, port, new_id); + } else if (!script->get_output_sequence_ports_connected(port_action_node).has(port)) { + undo_redo->add_do_method(script.ptr(), "sequence_connect", port_action_node, port, new_id); + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", port_action_node, port, new_id); break; } } @@ -3884,9 +3473,9 @@ void VisualScriptEditor::_selected_new_virtual_method(const String &p_text, cons Ref<VisualScriptFunction> func_node; func_node.instance(); func_node->set_name(name); - + int fn_id = script->get_available_id(); undo_redo->create_action(TTR("Add Function")); - undo_redo->add_do_method(script.ptr(), "add_function", name); + undo_redo->add_do_method(script.ptr(), "add_function", name, fn_id); for (int i = 0; i < minfo.arguments.size(); i++) { func_node->add_argument(minfo.arguments[i].type, minfo.arguments[i].name, -1, minfo.arguments[i].hint, minfo.arguments[i].hint_string); @@ -3894,14 +3483,17 @@ void VisualScriptEditor::_selected_new_virtual_method(const String &p_text, cons Vector2 ofs = _get_available_pos(); - undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id(), func_node, ofs); + undo_redo->add_do_method(script.ptr(), "add_node", fn_id, func_node, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", fn_id); if (minfo.return_val.type != Variant::NIL || minfo.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT) { Ref<VisualScriptReturn> ret_node; ret_node.instance(); ret_node->set_return_type(minfo.return_val.type); ret_node->set_enable_return_value(true); ret_node->set_name(name); - undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id() + 1, ret_node, _get_available_pos(false, ofs + Vector2(500, 0))); + int nid = script->get_available_id() + 1; + undo_redo->add_do_method(script.ptr(), "add_node", nid, ret_node, _get_available_pos(false, ofs + Vector2(500, 0))); + undo_redo->add_undo_method(script.ptr(), "remove_node", nid); } undo_redo->add_undo_method(script.ptr(), "remove_function", name); @@ -3916,21 +3508,16 @@ void VisualScriptEditor::_selected_new_virtual_method(const String &p_text, cons } void VisualScriptEditor::_cancel_connect_node() { - // ensure the cancel is done + // Ensure the cancel is done. port_action_new_node = -1; } -int VisualScriptEditor::_create_new_node_from_name(const String &p_text, const Vector2 &p_point, const StringName &p_func) { - StringName func = default_func; - if (p_func != StringName()) { - func = p_func; - } - +int VisualScriptEditor::_create_new_node_from_name(const String &p_text, const Vector2 &p_point) { Ref<VisualScriptNode> vnode = VisualScriptLanguage::singleton->create_node_from_name(p_text); int new_id = script->get_available_id(); undo_redo->create_action(TTR("Add Node")); - undo_redo->add_do_method(script.ptr(), "add_node", func, new_id, vnode, p_point); - undo_redo->add_undo_method(script.ptr(), "remove_node", func, new_id); + undo_redo->add_do_method(script.ptr(), "add_node", new_id, vnode, p_point); + undo_redo->add_undo_method(script.ptr(), "remove_node", new_id); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); @@ -3938,7 +3525,7 @@ int VisualScriptEditor::_create_new_node_from_name(const String &p_text, const V } void VisualScriptEditor::_default_value_changed() { - Ref<VisualScriptNode> vsn = script->get_node(_get_function_of_node(editing_id), editing_id); + Ref<VisualScriptNode> vsn = script->get_node(editing_id); if (vsn.is_null()) { return; } @@ -3953,7 +3540,7 @@ void VisualScriptEditor::_default_value_changed() { } void VisualScriptEditor::_default_value_edited(Node *p_button, int p_id, int p_input_port) { - Ref<VisualScriptNode> vsn = script->get_node(_get_function_of_node(p_id), p_id); + Ref<VisualScriptNode> vsn = script->get_node(p_id); if (vsn.is_null()) { return; } @@ -3972,15 +3559,15 @@ void VisualScriptEditor::_default_value_edited(Node *p_button, int p_id, int p_i if (pinfo.type == Variant::NODE_PATH) { Node *edited_scene = get_tree()->get_edited_scene_root(); - if (edited_scene) { // Fixing an old crash bug ( Visual Script Crashes on editing NodePath with an empty scene open) + if (edited_scene) { // Fixing an old crash bug ( Visual Script Crashes on editing NodePath with an empty scene open). Node *script_node = _find_script_node(edited_scene, edited_scene, script); if (script_node) { - //pick a node relative to the script, IF the script exists + // Pick a node relative to the script, IF the script exists. pinfo.hint = PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE; pinfo.hint_string = script_node->get_path(); } else { - //pick a path relative to edited scene + // Pick a path relative to edited scene. pinfo.hint = PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE; pinfo.hint_string = get_tree()->get_edited_scene_root()->get_path(); } @@ -4078,11 +3665,8 @@ void VisualScriptEditor::_graph_ofs_changed(const Vector2 &p_ofs) { updating_graph = true; - // Just use the default func for all the properties that need to be handled for drawing rather than adding to the Visual Script Class - if (script->has_function(default_func)) { - script->set_function_scroll(default_func, graph->get_scroll_ofs() / EDSCALE); - script->set_edited(true); - } + script->set_scroll(graph->get_scroll_ofs() / EDSCALE); + script->set_edited(true); updating_graph = false; } @@ -4090,10 +3674,7 @@ void VisualScriptEditor::_comment_node_resized(const Vector2 &p_new_size, int p_ if (updating_graph) { return; } - - StringName func = _get_function_of_node(p_node); - - Ref<VisualScriptComment> vsc = script->get_node(func, p_node); + Ref<VisualScriptComment> vsc = script->get_node(p_node); if (vsc.is_null()) { return; } @@ -4131,8 +3712,7 @@ void VisualScriptEditor::_menu_option(int p_what) { if (gn) { if (gn->is_selected()) { int id = String(gn->get_name()).to_int(); - StringName func = _get_function_of_node(id); - Ref<VisualScriptNode> vsn = script->get_node(func, id); + Ref<VisualScriptNode> vsn = script->get_node(id); if (vsn.is_valid()) { vsn->set_breakpoint(!vsn->is_breakpoint()); reselect.push_back(gn->get_name()); @@ -4154,58 +3734,47 @@ void VisualScriptEditor::_menu_option(int p_what) { } break; case EDIT_COPY_NODES: case EDIT_CUT_NODES: { - if (!script->has_function(default_func)) { - break; - } - clipboard->nodes.clear(); clipboard->data_connections.clear(); clipboard->sequence_connections.clear(); - Set<String> funcs; for (int i = 0; i < graph->get_child_count(); i++) { GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i)); if (gn) { if (gn->is_selected()) { - int id = String(gn->get_name()).to_int(); - StringName func = _get_function_of_node(id); - Ref<VisualScriptNode> node = script->get_node(func, id); + int id = gn->get_name().operator String().to_int(); + Ref<VisualScriptNode> node = script->get_node(id); if (Object::cast_to<VisualScriptFunction>(*node)) { EditorNode::get_singleton()->show_warning(TTR("Can't copy the function node.")); return; } if (node.is_valid()) { clipboard->nodes[id] = node->duplicate(true); - clipboard->nodes_positions[id] = script->get_node_position(func, id); - funcs.insert(String(func)); + clipboard->nodes_positions[id] = script->get_node_position(id); } } } } - if (clipboard->nodes.empty()) { + if (clipboard->nodes.is_empty()) { break; } - for (Set<String>::Element *F = funcs.front(); F; F = F->next()) { - List<VisualScript::SequenceConnection> sequence_connections; - - script->get_sequence_connection_list(F->get(), &sequence_connections); + List<VisualScript::SequenceConnection> sequence_connections; + script->get_sequence_connection_list(&sequence_connections); - for (List<VisualScript::SequenceConnection>::Element *E = sequence_connections.front(); E; E = E->next()) { - if (clipboard->nodes.has(E->get().from_node) && clipboard->nodes.has(E->get().to_node)) { - clipboard->sequence_connections.insert(E->get()); - } + for (List<VisualScript::SequenceConnection>::Element *E = sequence_connections.front(); E; E = E->next()) { + if (clipboard->nodes.has(E->get().from_node) && clipboard->nodes.has(E->get().to_node)) { + clipboard->sequence_connections.insert(E->get()); } + } - List<VisualScript::DataConnection> data_connections; - - script->get_data_connection_list(F->get(), &data_connections); + List<VisualScript::DataConnection> data_connections; + script->get_data_connection_list(&data_connections); - for (List<VisualScript::DataConnection>::Element *E = data_connections.front(); E; E = E->next()) { - if (clipboard->nodes.has(E->get().from_node) && clipboard->nodes.has(E->get().to_node)) { - clipboard->data_connections.insert(E->get()); - } + for (List<VisualScript::DataConnection>::Element *E = data_connections.front(); E; E = E->next()) { + if (clipboard->nodes.has(E->get().from_node) && clipboard->nodes.has(E->get().to_node)) { + clipboard->data_connections.insert(E->get()); } } if (p_what == EDIT_CUT_NODES) { @@ -4214,11 +3783,7 @@ void VisualScriptEditor::_menu_option(int p_what) { } break; case EDIT_PASTE_NODES: { - if (!script->has_function(default_func)) { - break; - } - - if (clipboard->nodes.empty()) { + if (clipboard->nodes.is_empty()) { EditorNode::get_singleton()->show_warning(TTR("Clipboard is empty!")); break; } @@ -4233,15 +3798,11 @@ void VisualScriptEditor::_menu_option(int p_what) { Set<Vector2> existing_positions; { - List<StringName> functions; - script->get_function_list(&functions); - for (List<StringName>::Element *F = functions.front(); F; F = F->next()) { - List<int> nodes; - script->get_node_list(F->get(), &nodes); - for (List<int>::Element *E = nodes.front(); E; E = E->next()) { - Vector2 pos = script->get_node_position(F->get(), E->get()).snapped(Vector2(2, 2)); - existing_positions.insert(pos); - } + List<int> nodes; + script->get_node_list(&nodes); + for (List<int>::Element *E = nodes.front(); E; E = E->next()) { + Vector2 pos = script->get_node_position(E->get()).snapped(Vector2(2, 2)); + existing_positions.insert(pos); } } @@ -4259,18 +3820,18 @@ void VisualScriptEditor::_menu_option(int p_what) { paste_pos += Vector2(20, 20) * EDSCALE; } - undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, node, paste_pos); - undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id); + undo_redo->add_do_method(script.ptr(), "add_node", new_id, node, paste_pos); + undo_redo->add_undo_method(script.ptr(), "remove_node", new_id); } for (Set<VisualScript::SequenceConnection>::Element *E = clipboard->sequence_connections.front(); E; E = E->next()) { - undo_redo->add_do_method(script.ptr(), "sequence_connect", default_func, remap[E->get().from_node], E->get().from_output, remap[E->get().to_node]); - undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", default_func, remap[E->get().from_node], E->get().from_output, remap[E->get().to_node]); + undo_redo->add_do_method(script.ptr(), "sequence_connect", remap[E->get().from_node], E->get().from_output, remap[E->get().to_node]); + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", remap[E->get().from_node], E->get().from_output, remap[E->get().to_node]); } for (Set<VisualScript::DataConnection>::Element *E = clipboard->data_connections.front(); E; E = E->next()) { - undo_redo->add_do_method(script.ptr(), "data_connect", default_func, remap[E->get().from_node], E->get().from_port, remap[E->get().to_node], E->get().to_port); - undo_redo->add_undo_method(script.ptr(), "data_disconnect", default_func, remap[E->get().from_node], E->get().from_port, remap[E->get().to_node], E->get().to_port); + undo_redo->add_do_method(script.ptr(), "data_connect", remap[E->get().from_node], E->get().from_port, remap[E->get().to_node], E->get().to_port); + undo_redo->add_undo_method(script.ptr(), "data_disconnect", remap[E->get().from_node], E->get().from_port, remap[E->get().to_node], E->get().to_port); } undo_redo->add_do_method(this, "_update_graph"); @@ -4287,7 +3848,7 @@ void VisualScriptEditor::_menu_option(int p_what) { } } break; case EDIT_CREATE_FUNCTION: { - StringName function = ""; + // Create Function. Map<int, Ref<VisualScriptNode>> nodes; Set<int> selections; for (int i = 0; i < graph->get_child_count(); i++) { @@ -4295,20 +3856,14 @@ void VisualScriptEditor::_menu_option(int p_what) { if (gn) { if (gn->is_selected()) { int id = String(gn->get_name()).to_int(); - StringName func = _get_function_of_node(id); - Ref<VisualScriptNode> node = script->get_node(func, id); + Ref<VisualScriptNode> node = script->get_node(id); if (Object::cast_to<VisualScriptFunction>(*node)) { EditorNode::get_singleton()->show_warning(TTR("Can't create function with a function node.")); return; } if (node.is_valid()) { - if (func != function && function != StringName("")) { - EditorNode::get_singleton()->show_warning(TTR("Can't create function of nodes from nodes of multiple functions.")); - return; - } nodes.insert(id, node); selections.insert(id); - function = func; } } } @@ -4327,7 +3882,7 @@ void VisualScriptEditor::_menu_option(int p_what) { int start_node = -1; Set<int> end_nodes; if (nodes.size() == 1) { - Ref<VisualScriptNode> nd = script->get_node(function, nodes.front()->key()); + Ref<VisualScriptNode> nd = script->get_node(nodes.front()->key()); if (nd.is_valid() && nd->has_input_sequence_port()) { start_node = nodes.front()->key(); } else { @@ -4336,29 +3891,29 @@ void VisualScriptEditor::_menu_option(int p_what) { } } else { List<VisualScript::SequenceConnection> seqs; - script->get_sequence_connection_list(function, &seqs); + script->get_sequence_connection_list(&seqs); if (seqs.size() == 0) { - // in case there are no sequence connections - // select the top most node cause that's probably how - // the user wants to connect the nodes + // In case there are no sequence connections, + // select the top most node cause that's probably how, + // the user wants to connect the nodes. int top_nd = -1; Vector2 top; for (Map<int, Ref<VisualScriptNode>>::Element *E = nodes.front(); E; E = E->next()) { - Ref<VisualScriptNode> nd = script->get_node(function, E->key()); + Ref<VisualScriptNode> nd = script->get_node(E->key()); if (nd.is_valid() && nd->has_input_sequence_port()) { if (top_nd < 0) { top_nd = E->key(); - top = script->get_node_position(function, top_nd); + top = script->get_node_position(top_nd); } - Vector2 pos = script->get_node_position(function, E->key()); + Vector2 pos = script->get_node_position(E->key()); if (top.y > pos.y) { top_nd = E->key(); top = pos; } } } - Ref<VisualScriptNode> nd = script->get_node(function, top_nd); + Ref<VisualScriptNode> nd = script->get_node(top_nd); if (nd.is_valid() && nd->has_input_sequence_port()) { start_node = top_nd; } else { @@ -4366,7 +3921,7 @@ void VisualScriptEditor::_menu_option(int p_what) { return; } } else { - // pick the node with input sequence + // Pick the node with input sequence. Set<int> nodes_from; Set<int> nodes_to; for (List<VisualScript::SequenceConnection>::Element *E = seqs.front(); E; E = E->next()) { @@ -4387,13 +3942,13 @@ void VisualScriptEditor::_menu_option(int p_what) { nodes_to.insert(E->get().to_node); } - // to use to add return nodes + // To use to add return nodes. _get_ends(start_node, seqs, selections, end_nodes); if (start_node == -1) { - // if we still don't have a start node then - // run through the nodes and select the first tree node - // ie node without any input sequence but output sequence + // If we still don't have a start node then, + // run through the nodes and select the first tree node, + // ie node without any input sequence but output sequence. for (Set<int>::Element *E = nodes_from.front(); E; E = E->next()) { if (!nodes_to.has(E->get())) { start_node = E->get(); @@ -4404,20 +3959,20 @@ void VisualScriptEditor::_menu_option(int p_what) { } if (start_node == -1) { - return; // this should not happen, but just in case something goes wrong + return; // This should not happen, but just in case something goes wrong. } List<Variant::Type> inputs; // input types List<Pair<int, int>> input_connections; { List<VisualScript::DataConnection> dats; - script->get_data_connection_list(function, &dats); + script->get_data_connection_list(&dats); for (List<VisualScript::DataConnection>::Element *E = dats.front(); E; E = E->next()) { if (nodes.has(E->get().from_node) && nodes.has(E->get().to_node)) { datamove.insert(E->get()); } else if (!nodes.has(E->get().from_node) && nodes.has(E->get().to_node)) { - // add all these as inputs for the Function - Ref<VisualScriptNode> node = script->get_node(function, E->get().to_node); + // Add all these as inputs for the Function. + Ref<VisualScriptNode> node = script->get_node(E->get().to_node); if (node.is_valid()) { dataext.insert(E->get()); PropertyInfo pi = node->get_input_value_port_info(E->get().to_port); @@ -4429,59 +3984,55 @@ void VisualScriptEditor::_menu_option(int p_what) { } } } - - String new_fn = _validate_name("new_function"); - - Vector2 ofs = _get_available_pos(false, script->get_node_position(function, start_node) - Vector2(80, 150)); - - Ref<VisualScriptFunction> func_node; - func_node.instance(); - func_node->set_name(new_fn); - - undo_redo->create_action(TTR("Create Function")); - - undo_redo->add_do_method(script.ptr(), "add_function", new_fn); int fn_id = script->get_available_id(); - undo_redo->add_do_method(script.ptr(), "add_node", new_fn, fn_id, func_node, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_function", new_fn); - undo_redo->add_do_method(this, "_update_members"); - undo_redo->add_undo_method(this, "_update_members"); - undo_redo->add_do_method(this, "emit_signal", "edited_script_changed"); - undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed"); - - // Move the nodes + { + String new_fn = _validate_name("new_function"); - for (Map<int, Ref<VisualScriptNode>>::Element *E = nodes.front(); E; E = E->next()) { - undo_redo->add_do_method(script.ptr(), "remove_node", function, E->key()); - undo_redo->add_do_method(script.ptr(), "add_node", new_fn, E->key(), E->get(), script->get_node_position(function, E->key())); + Vector2 ofs = _get_available_pos(false, script->get_node_position(start_node) - Vector2(80, 150)); - // undo_redo->add_undo_method(script.ptr(), "remove_node", new_fn, E->key()); not needed cause we already remove the function :P - undo_redo->add_undo_method(script.ptr(), "add_node", function, E->key(), E->get(), script->get_node_position(function, E->key())); - } + Ref<VisualScriptFunction> func_node; + func_node.instance(); + func_node->set_name(new_fn); - for (Set<VisualScript::SequenceConnection>::Element *E = seqmove.front(); E; E = E->next()) { - undo_redo->add_do_method(script.ptr(), "sequence_connect", new_fn, E->get().from_node, E->get().from_output, E->get().to_node); - undo_redo->add_undo_method(script.ptr(), "sequence_connect", function, E->get().from_node, E->get().from_output, E->get().to_node); - } + undo_redo->create_action(TTR("Create Function")); - for (Set<VisualScript::DataConnection>::Element *E = datamove.front(); E; E = E->next()) { - undo_redo->add_do_method(script.ptr(), "data_connect", new_fn, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); - undo_redo->add_undo_method(script.ptr(), "data_connect", function, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); + undo_redo->add_do_method(script.ptr(), "add_function", new_fn, fn_id); + undo_redo->add_do_method(script.ptr(), "add_node", fn_id, func_node, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_function", new_fn); + undo_redo->add_undo_method(script.ptr(), "remove_node", fn_id); + undo_redo->add_do_method(this, "_update_members"); + undo_redo->add_undo_method(this, "_update_members"); + undo_redo->add_do_method(this, "emit_signal", "edited_script_changed"); + undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed"); + // Might make the system more intelligent by checking port from info. + int i = 0; + List<Pair<int, int>>::Element *F = input_connections.front(); + for (List<Variant::Type>::Element *E = inputs.front(); E && F; E = E->next(), F = F->next()) { + func_node->add_argument(E->get(), "arg_" + String::num_int64(i), i); + undo_redo->add_do_method(script.ptr(), "data_connect", fn_id, i, F->get().first, F->get().second); + i++; // increment i + } + // Ensure Preview Selection is of newly created function node. + if (selections.size()) { + EditorNode::get_singleton()->push_item(func_node.ptr()); + } } + // Move the nodes. - // Add undo for external connections as well so that it's easier to revert back and forth - // these didn't require do methods as it's already handled internally by other do calls + // Handles reconnection of sequence connections on undo, start here in case of issues. for (Set<VisualScript::SequenceConnection>::Element *E = seqext.front(); E; E = E->next()) { - undo_redo->add_undo_method(script.ptr(), "sequence_connect", function, E->get().from_node, E->get().from_output, E->get().to_node); + undo_redo->add_do_method(script.ptr(), "sequence_disconnect", E->get().from_node, E->get().from_output, E->get().to_node); + undo_redo->add_undo_method(script.ptr(), "sequence_connect", E->get().from_node, E->get().from_output, E->get().to_node); } for (Set<VisualScript::DataConnection>::Element *E = dataext.front(); E; E = E->next()) { - undo_redo->add_undo_method(script.ptr(), "data_connect", function, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); + undo_redo->add_do_method(script.ptr(), "data_disconnect", E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); + undo_redo->add_undo_method(script.ptr(), "data_connect", E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); } - // I don't really think we need support for non sequenced functions at this moment - undo_redo->add_do_method(script.ptr(), "sequence_connect", new_fn, fn_id, 0, start_node); + // I don't really think we need support for non sequenced functions at this moment. + undo_redo->add_do_method(script.ptr(), "sequence_connect", fn_id, 0, start_node); - // end nodes are mapped to the return nodes with data connections if possible + // Could fail with the new changes, start here when searching for bugs in create function shortcut. int m = 1; for (Set<int>::Element *G = end_nodes.front(); G; G = G->next()) { Ref<VisualScriptReturn> ret_node; @@ -4489,36 +4040,27 @@ void VisualScriptEditor::_menu_option(int p_what) { int ret_id = fn_id + (m++); selections.insert(ret_id); - Vector2 ofsi = _get_available_pos(false, script->get_node_position(function, G->get()) + Vector2(80, -100)); - undo_redo->add_do_method(script.ptr(), "add_node", new_fn, ret_id, ret_node, ofsi); - undo_redo->add_undo_method(script.ptr(), "remove_node", new_fn, ret_id); + Vector2 ofsi = _get_available_pos(false, script->get_node_position(G->get()) + Vector2(80, -100)); + undo_redo->add_do_method(script.ptr(), "add_node", ret_id, ret_node, ofsi); + undo_redo->add_undo_method(script.ptr(), "remove_node", ret_id); - undo_redo->add_do_method(script.ptr(), "sequence_connect", new_fn, G->get(), 0, ret_id); - // add data outputs from each of the end_nodes - Ref<VisualScriptNode> vsn = script->get_node(function, G->get()); + undo_redo->add_do_method(script.ptr(), "sequence_connect", G->get(), 0, ret_id); + // Add data outputs from each of the end_nodes. + Ref<VisualScriptNode> vsn = script->get_node(G->get()); if (vsn.is_valid() && vsn->get_output_value_port_count() > 0) { ret_node->set_enable_return_value(true); - // use the zeroth data port cause that's the likely one that is planned to be used + // Use the zeroth data port cause that's the likely one that is planned to be used. ret_node->set_return_type(vsn->get_output_value_port_info(0).type); - undo_redo->add_do_method(script.ptr(), "data_connect", new_fn, G->get(), 0, ret_id, 0); + undo_redo->add_do_method(script.ptr(), "data_connect", G->get(), 0, ret_id, 0); } } - // * might make the system more intelligent by checking port from info. - int i = 0; - List<Pair<int, int>>::Element *F = input_connections.front(); - for (List<Variant::Type>::Element *E = inputs.front(); E && F; E = E->next(), F = F->next()) { - func_node->add_argument(E->get(), "arg_" + String::num_int64(i), i); - undo_redo->add_do_method(script.ptr(), "data_connect", new_fn, fn_id, i, F->get().first, F->get().second); - i++; // increment i - } - undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); - // make sure all Nodes get marked for selection so that they can be moved together + // Make sure all Nodes get marked for selection so that they can be moved together. selections.insert(fn_id); for (int k = 0; k < graph->get_child_count(); k++) { GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(k)); @@ -4528,11 +4070,6 @@ void VisualScriptEditor::_menu_option(int p_what) { } } - // Ensure Preview Selection is of newly created function node - if (selections.size()) { - EditorNode::get_singleton()->push_item(func_node.ptr()); - } - } break; case REFRESH_GRAPH: { _update_graph(); @@ -4540,16 +4077,16 @@ void VisualScriptEditor::_menu_option(int p_what) { } } -// this is likely going to be very slow and I am not sure if I should keep it -// but I hope that it will not be a problem considering that we won't be creating functions so frequently -// and cyclic connections would be a problem but hopefully we won't let them get to this point +// This is likely going to be very slow and I am not sure if I should keep it, +// but I hope that it will not be a problem considering that we won't be creating functions so frequently, +// and cyclic connections would be a problem but hopefully we won't let them get to this point. void VisualScriptEditor::_get_ends(int p_node, const List<VisualScript::SequenceConnection> &p_seqs, const Set<int> &p_selected, Set<int> &r_end_nodes) { for (const List<VisualScript::SequenceConnection>::Element *E = p_seqs.front(); E; E = E->next()) { int from = E->get().from_node; int to = E->get().to_node; if (from == p_node && p_selected.has(to)) { - // this is an interior connection move forward to the to node + // This is an interior connection move forward to the to node. _get_ends(to, p_seqs, p_selected, r_end_nodes); } else if (from == p_node && !p_selected.has(to)) { r_end_nodes.insert(from); @@ -4609,34 +4146,29 @@ void VisualScriptEditor::_member_option(int p_option) { switch (member_type) { case MEMBER_FUNCTION: { if (p_option == MEMBER_REMOVE) { - //delete the function + // Delete the function. String name = member_name; - + List<String> lst; + int fn_node = script->get_function_node_id(name); undo_redo->create_action(TTR("Remove Function")); undo_redo->add_do_method(script.ptr(), "remove_function", name); - undo_redo->add_undo_method(script.ptr(), "add_function", name); - List<int> nodes; - script->get_node_list(name, &nodes); - for (List<int>::Element *E = nodes.front(); E; E = E->next()) { - undo_redo->add_undo_method(script.ptr(), "add_node", name, E->get(), script->get_node(name, E->get()), script->get_node_position(name, E->get())); - } - - List<VisualScript::SequenceConnection> seq_connections; - - script->get_sequence_connection_list(name, &seq_connections); - - for (List<VisualScript::SequenceConnection>::Element *E = seq_connections.front(); E; E = E->next()) { - undo_redo->add_undo_method(script.ptr(), "sequence_connect", name, E->get().from_node, E->get().from_output, E->get().to_node); + undo_redo->add_do_method(script.ptr(), "remove_node", fn_node); + undo_redo->add_undo_method(script.ptr(), "add_function", name, fn_node); + undo_redo->add_undo_method(script.ptr(), "add_node", fn_node, script->get_node(fn_node), script->get_node_position(fn_node)); + List<VisualScript::SequenceConnection> seqcons; + script->get_sequence_connection_list(&seqcons); + for (const List<VisualScript::SequenceConnection>::Element *E = seqcons.front(); E; E = E->next()) { + if (E->get().from_node == fn_node) { + undo_redo->add_undo_method(script.ptr(), "sequence_connect", fn_node, E->get().from_output, E->get().to_node); + } } - - List<VisualScript::DataConnection> data_connections; - - script->get_data_connection_list(name, &data_connections); - - for (List<VisualScript::DataConnection>::Element *E = data_connections.front(); E; E = E->next()) { - undo_redo->add_undo_method(script.ptr(), "data_connect", name, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); + List<VisualScript::DataConnection> datcons; + script->get_data_connection_list(&datcons); + for (const List<VisualScript::DataConnection>::Element *E = datcons.front(); E; E = E->next()) { + if (E->get().from_node == fn_node) { + undo_redo->add_undo_method(script.ptr(), "data_connect", fn_node, E->get().from_port, E->get().to_node, E->get().to_port); + } } - undo_redo->add_do_method(this, "_update_members"); undo_redo->add_undo_method(this, "_update_members"); undo_redo->add_do_method(this, "_update_graph"); @@ -4786,7 +4318,7 @@ VisualScriptEditor::VisualScriptEditor() { graph = memnew(GraphEdit); add_child(graph); graph->set_v_size_flags(Control::SIZE_EXPAND_FILL); - graph->set_anchors_and_margins_preset(Control::PRESET_WIDE); + graph->set_anchors_and_offsets_preset(Control::PRESET_WIDE); graph->connect("node_selected", callable_mp(this, &VisualScriptEditor::_node_selected)); graph->connect("begin_node_move", callable_mp(this, &VisualScriptEditor::_begin_node_move)); graph->connect("end_node_move", callable_mp(this, &VisualScriptEditor::_end_node_move)); @@ -4858,8 +4390,8 @@ VisualScriptEditor::VisualScriptEditor() { function_create_dialog = memnew(ConfirmationDialog); function_create_dialog->set_title(TTR("Create Function")); function_create_dialog->add_child(function_vb); - function_create_dialog->get_ok()->set_text(TTR("Create")); - function_create_dialog->get_ok()->connect("pressed", callable_mp(this, &VisualScriptEditor::_create_function)); + function_create_dialog->get_ok_button()->set_text(TTR("Create")); + function_create_dialog->get_ok_button()->connect("pressed", callable_mp(this, &VisualScriptEditor::_create_function)); add_child(function_create_dialog); select_func_text = memnew(Label); @@ -4870,9 +4402,9 @@ VisualScriptEditor::VisualScriptEditor() { add_child(select_func_text); hint_text = memnew(Label); - hint_text->set_anchor_and_margin(MARGIN_TOP, ANCHOR_END, -100); - hint_text->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, 0); - hint_text->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, 0); + hint_text->set_anchor_and_offset(SIDE_TOP, ANCHOR_END, -100); + hint_text->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0); + hint_text->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0); hint_text->set_align(Label::ALIGN_CENTER); hint_text->set_valign(Label::VALIGN_CENTER); graph->add_child(hint_text); @@ -4902,7 +4434,7 @@ VisualScriptEditor::VisualScriptEditor() { graph->connect("connection_to_empty", callable_mp(this, &VisualScriptEditor::_graph_connect_to_empty)); edit_signal_dialog = memnew(AcceptDialog); - edit_signal_dialog->get_ok()->set_text(TTR("Close")); + edit_signal_dialog->get_ok_button()->set_text(TTR("Close")); add_child(edit_signal_dialog); signal_editor = memnew(VisualScriptEditorSignalEdit); @@ -4912,7 +4444,7 @@ VisualScriptEditor::VisualScriptEditor() { edit_signal_edit->edit(signal_editor); edit_variable_dialog = memnew(AcceptDialog); - edit_variable_dialog->get_ok()->set_text(TTR("Close")); + edit_variable_dialog->get_ok_button()->set_text(TTR("Close")); add_child(edit_variable_dialog); variable_editor = memnew(VisualScriptEditorVariableEdit); @@ -4944,7 +4476,7 @@ VisualScriptEditor::VisualScriptEditor() { new_connect_node_select = memnew(VisualScriptPropertySelector); add_child(new_connect_node_select); new_connect_node_select->connect("selected", callable_mp(this, &VisualScriptEditor::_selected_connect_node)); - new_connect_node_select->get_cancel()->connect("pressed", callable_mp(this, &VisualScriptEditor::_cancel_connect_node)); + new_connect_node_select->get_cancel_button()->connect("pressed", callable_mp(this, &VisualScriptEditor::_cancel_connect_node)); new_virtual_method_select = memnew(VisualScriptPropertySelector); add_child(new_virtual_method_select); diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h index 5610e6b1b4..bb6f194286 100644 --- a/modules/visual_script/visual_script_editor.h +++ b/modules/visual_script/visual_script_editor.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -136,8 +136,6 @@ class VisualScriptEditor : public ScriptEditorBase { }; HashMap<StringName, Ref<StyleBox>> node_styles; - StringName edited_func; - StringName default_func; void _update_graph_connections(); void _update_graph(int p_only_id = -1); @@ -176,7 +174,7 @@ class VisualScriptEditor : public ScriptEditorBase { Vector2 mouse_up_position; - void _port_action_menu(int p_option, const StringName &p_func); + void _port_action_menu(int p_option); void connect_data(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode, int new_id); @@ -184,13 +182,13 @@ class VisualScriptEditor : public ScriptEditorBase { void connect_seq(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode_new, int new_id); void _cancel_connect_node(); - int _create_new_node_from_name(const String &p_text, const Vector2 &p_point, const StringName &p_func = StringName()); + int _create_new_node_from_name(const String &p_text, const Vector2 &p_point); void _selected_new_virtual_method(const String &p_text, const String &p_category, const bool p_connecting); int error_line; void _node_selected(Node *p_node); - void _center_on_node(const StringName &p_func, int p_id); + void _center_on_node(int p_id); void _node_filter_changed(const String &p_text); void _change_base_type_callback(); @@ -201,7 +199,7 @@ class VisualScriptEditor : public ScriptEditorBase { void _begin_node_move(); void _end_node_move(); - void _move_node(const StringName &p_func, int p_id, const Vector2 &p_to); + void _move_node(int p_id, const Vector2 &p_to); void _get_ends(int p_node, const List<VisualScript::SequenceConnection> &p_seqs, const Set<int> &p_selected, Set<int> &r_end_nodes); @@ -211,7 +209,7 @@ class VisualScriptEditor : public ScriptEditorBase { void _graph_disconnected(const String &p_from, int p_from_slot, const String &p_to, int p_to_slot); void _graph_connect_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_pos); - void _node_ports_changed(const String &p_func, int p_id); + void _node_ports_changed(int p_id); void _node_create(); void _update_available_nodes(); @@ -228,10 +226,8 @@ class VisualScriptEditor : public ScriptEditorBase { void _port_name_focus_out(const Node *p_name_box, int p_id, int p_port, bool is_input); Vector2 _get_available_pos(bool centered = true, Vector2 ofs = Vector2()) const; - StringName _get_function_of_node(int p_id) const; - void _move_nodes_with_rescan(const StringName &p_func_from, const StringName &p_func_to, int p_id); - bool node_has_sequence_connections(const StringName &p_func, int p_id); + bool node_has_sequence_connections(int p_id); void _generic_search(String p_base_type = "", Vector2 pos = Vector2(), bool node_centered = false); diff --git a/modules/visual_script/visual_script_expression.cpp b/modules/visual_script/visual_script_expression.cpp index 10a18dfd5e..9596fda95c 100644 --- a/modules/visual_script/visual_script_expression.cpp +++ b/modules/visual_script/visual_script_expression.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/visual_script/visual_script_expression.h b/modules/visual_script/visual_script_expression.h index 2b3b25842d..7fe665769d 100644 --- a/modules/visual_script/visual_script_expression.h +++ b/modules/visual_script/visual_script_expression.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/visual_script/visual_script_flow_control.cpp b/modules/visual_script/visual_script_flow_control.cpp index 36c756fc58..0049e254c4 100644 --- a/modules/visual_script/visual_script_flow_control.cpp +++ b/modules/visual_script/visual_script_flow_control.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/visual_script/visual_script_flow_control.h b/modules/visual_script/visual_script_flow_control.h index 1d0d6d103b..46a72bb92d 100644 --- a/modules/visual_script/visual_script_flow_control.h +++ b/modules/visual_script/visual_script_flow_control.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp index b2aa42ef97..d016b938de 100644 --- a/modules/visual_script/visual_script_func_nodes.cpp +++ b/modules/visual_script/visual_script_func_nodes.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/visual_script/visual_script_func_nodes.h b/modules/visual_script/visual_script_func_nodes.h index 8372df561f..2ff9b7a981 100644 --- a/modules/visual_script/visual_script_func_nodes.h +++ b/modules/visual_script/visual_script_func_nodes.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp index edec270adc..ae2b548f21 100644 --- a/modules/visual_script/visual_script_nodes.cpp +++ b/modules/visual_script/visual_script_nodes.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/visual_script/visual_script_nodes.h b/modules/visual_script/visual_script_nodes.h index b6061f8838..ae5e04d096 100644 --- a/modules/visual_script/visual_script_nodes.h +++ b/modules/visual_script/visual_script_nodes.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/visual_script/visual_script_property_selector.cpp b/modules/visual_script/visual_script_property_selector.cpp index 875270e74f..862cac5c67 100644 --- a/modules/visual_script/visual_script_property_selector.cpp +++ b/modules/visual_script/visual_script_property_selector.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,6 +31,7 @@ #include "visual_script_property_selector.h" #include "core/os/keyboard.h" +#include "editor/doc_tools.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "modules/visual_script/visual_script.h" @@ -309,7 +310,7 @@ void VisualScriptPropertySelector::_update_search() { found = true; } - get_ok()->set_disabled(root->get_children() == nullptr); + get_ok_button()->set_disabled(root->get_children() == nullptr); } void VisualScriptPropertySelector::create_visualscript_item(const String &name, TreeItem *const root, const String &search_input, const String &text) { @@ -357,7 +358,7 @@ void VisualScriptPropertySelector::get_visual_node_names(const String &root_filt continue; } - bool in_modifier = p_modifiers.empty(); + bool in_modifier = p_modifiers.is_empty(); for (Set<String>::Element *F = p_modifiers.front(); F && in_modifier; F = F->next()) { if (E->get().findn(F->get()) != -1) { in_modifier = true; @@ -437,7 +438,7 @@ void VisualScriptPropertySelector::_item_selected() { class_type = base_type; } - DocData *dd = EditorHelp::get_doc_data(); + DocTools *dd = EditorHelp::get_doc_data(); String text; String at_class = class_type; @@ -704,8 +705,8 @@ VisualScriptPropertySelector::VisualScriptPropertySelector() { search_box->connect("gui_input", callable_mp(this, &VisualScriptPropertySelector::_sbox_input)); search_options = memnew(Tree); vbc->add_margin_child(TTR("Matches:"), search_options, true); - get_ok()->set_text(TTR("Open")); - get_ok()->set_disabled(true); + get_ok_button()->set_text(TTR("Open")); + get_ok_button()->set_disabled(true); register_text_enter(search_box); set_hide_on_ok(false); search_options->connect("item_activated", callable_mp(this, &VisualScriptPropertySelector::_confirmed)); diff --git a/modules/visual_script/visual_script_property_selector.h b/modules/visual_script/visual_script_property_selector.h index cc49b2863d..7a87f3d3ee 100644 --- a/modules/visual_script/visual_script_property_selector.h +++ b/modules/visual_script/visual_script_property_selector.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/visual_script/visual_script_yield_nodes.cpp b/modules/visual_script/visual_script_yield_nodes.cpp index dd07cc45a7..6c9af4e600 100644 --- a/modules/visual_script/visual_script_yield_nodes.cpp +++ b/modules/visual_script/visual_script_yield_nodes.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/visual_script/visual_script_yield_nodes.h b/modules/visual_script/visual_script_yield_nodes.h index 7a72211027..cc7ce0a1c6 100644 --- a/modules/visual_script/visual_script_yield_nodes.h +++ b/modules/visual_script/visual_script_yield_nodes.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/vorbis/SCsub b/modules/vorbis/SCsub index 05d46757d3..bc31fff066 100644 --- a/modules/vorbis/SCsub +++ b/modules/vorbis/SCsub @@ -8,9 +8,10 @@ Import("env_modules") env_vorbis = env_modules.Clone() -stub = True - # Thirdparty source files + +thirdparty_obj = [] + if env["builtin_libvorbis"]: thirdparty_dir = "#thirdparty/libvorbis/" thirdparty_sources = [ @@ -51,7 +52,16 @@ if env["builtin_libvorbis"]: env_thirdparty = env_vorbis.Clone() env_thirdparty.disable_warnings() - env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) + env.modules_sources += thirdparty_obj + + +# Godot source files + +module_obj = [] + +env_vorbis.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj -# Module files -env_vorbis.add_source_files(env.modules_sources, "register_types.cpp") +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/vorbis/register_types.cpp b/modules/vorbis/register_types.cpp index 8874b3887b..d3e77ea629 100644 --- a/modules/vorbis/register_types.cpp +++ b/modules/vorbis/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/vorbis/register_types.h b/modules/vorbis/register_types.h index 7fa0dfdeef..1497e6f5e4 100644 --- a/modules/vorbis/register_types.h +++ b/modules/vorbis/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webm/SCsub b/modules/webm/SCsub index 247b4ead37..44e80e2870 100644 --- a/modules/webm/SCsub +++ b/modules/webm/SCsub @@ -6,6 +6,9 @@ Import("env_modules") env_webm = env_modules.Clone() # Thirdparty source files + +thirdparty_obj = [] + thirdparty_dir = "#thirdparty/libsimplewebm/" thirdparty_sources = [ "libwebm/mkvparser/mkvparser.cc", @@ -31,7 +34,15 @@ if env["builtin_libvpx"]: env_thirdparty = env_webm.Clone() env_thirdparty.disable_warnings() -env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) +env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) +env.modules_sources += thirdparty_obj # Godot source files -env_webm.add_source_files(env.modules_sources, "*.cpp") + +module_obj = [] + +env_webm.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj + +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/webm/libvpx/SCsub b/modules/webm/libvpx/SCsub index d0744fa313..67d3f1bebd 100644 --- a/modules/webm/libvpx/SCsub +++ b/modules/webm/libvpx/SCsub @@ -278,7 +278,7 @@ if webm_cpu_x86: try: yasm_found = True subprocess.Popen([yasm_path, "--version"], stdout=devnull, stderr=devnull).communicate() - except: + except Exception: yasm_found = False if yasm_found: break diff --git a/modules/webm/register_types.cpp b/modules/webm/register_types.cpp index 6248787879..82157a71c9 100644 --- a/modules/webm/register_types.cpp +++ b/modules/webm/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webm/register_types.h b/modules/webm/register_types.h index 6a02e3a87a..d090fe745b 100644 --- a/modules/webm/register_types.h +++ b/modules/webm/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webm/video_stream_webm.cpp b/modules/webm/video_stream_webm.cpp index 2128b82e3f..5d8245c64c 100644 --- a/modules/webm/video_stream_webm.cpp +++ b/modules/webm/video_stream_webm.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webm/video_stream_webm.h b/modules/webm/video_stream_webm.h index 25675cb248..cb3cf58850 100644 --- a/modules/webm/video_stream_webm.h +++ b/modules/webm/video_stream_webm.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webp/SCsub b/modules/webp/SCsub index 58f2bb35e6..4c0c2f7893 100644 --- a/modules/webp/SCsub +++ b/modules/webp/SCsub @@ -6,6 +6,9 @@ Import("env_modules") env_webp = env_modules.Clone() # Thirdparty source files + +thirdparty_obj = [] + if env["builtin_libwebp"]: thirdparty_dir = "#thirdparty/libwebp/" thirdparty_sources = [ @@ -130,7 +133,16 @@ if env["builtin_libwebp"]: env_thirdparty = env_webp.Clone() env_thirdparty.disable_warnings() - env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) + env.modules_sources += thirdparty_obj + # Godot source files -env_webp.add_source_files(env.modules_sources, "*.cpp") + +module_obj = [] + +env_webp.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj + +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/webp/image_loader_webp.cpp b/modules/webp/image_loader_webp.cpp index b169687f25..b304c4824f 100644 --- a/modules/webp/image_loader_webp.cpp +++ b/modules/webp/image_loader_webp.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -39,7 +39,7 @@ #include <webp/encode.h> static Vector<uint8_t> _webp_lossy_pack(const Ref<Image> &p_image, float p_quality) { - ERR_FAIL_COND_V(p_image.is_null() || p_image->empty(), Vector<uint8_t>()); + ERR_FAIL_COND_V(p_image.is_null() || p_image->is_empty(), Vector<uint8_t>()); Ref<Image> img = p_image->duplicate(); if (img->detect_alpha()) { diff --git a/modules/webp/image_loader_webp.h b/modules/webp/image_loader_webp.h index 49a7407600..9ea3056a19 100644 --- a/modules/webp/image_loader_webp.h +++ b/modules/webp/image_loader_webp.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webp/register_types.cpp b/modules/webp/register_types.cpp index 0788b06309..ea9af72418 100644 --- a/modules/webp/register_types.cpp +++ b/modules/webp/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webp/register_types.h b/modules/webp/register_types.h index d574d7be1d..59d6894bf6 100644 --- a/modules/webp/register_types.h +++ b/modules/webp/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webrtc/SCsub b/modules/webrtc/SCsub index 4f870ddb2f..31b8a73bf2 100644 --- a/modules/webrtc/SCsub +++ b/modules/webrtc/SCsub @@ -3,8 +3,6 @@ Import("env") Import("env_modules") -# Thirdparty source files - env_webrtc = env_modules.Clone() use_gdnative = env_webrtc["module_gdnative_enabled"] diff --git a/modules/webrtc/library_godot_webrtc.js b/modules/webrtc/library_godot_webrtc.js index f725a10a6f..404a116716 100644 --- a/modules/webrtc/library_godot_webrtc.js +++ b/modules/webrtc/library_godot_webrtc.js @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -90,6 +90,7 @@ const GodotRTCDataChannel = { }, }, + godot_js_rtc_datachannel_ready_state_get__sig: 'ii', godot_js_rtc_datachannel_ready_state_get: function (p_id) { const ref = IDHandler.get(p_id); if (!ref) { @@ -109,6 +110,7 @@ const GodotRTCDataChannel = { } }, + godot_js_rtc_datachannel_send__sig: 'iiiii', godot_js_rtc_datachannel_send: function (p_id, p_buffer, p_length, p_raw) { const ref = IDHandler.get(p_id); if (!ref) { @@ -129,14 +131,17 @@ const GodotRTCDataChannel = { return 0; }, + godot_js_rtc_datachannel_is_ordered__sig: 'ii', godot_js_rtc_datachannel_is_ordered: function (p_id) { return IDHandler.get_prop(p_id, 'ordered', true); }, + godot_js_rtc_datachannel_id_get__sig: 'ii', godot_js_rtc_datachannel_id_get: function (p_id) { return IDHandler.get_prop(p_id, 'id', 65535); }, + godot_js_rtc_datachannel_max_packet_lifetime_get__sig: 'ii', godot_js_rtc_datachannel_max_packet_lifetime_get: function (p_id) { const ref = IDHandler.get(p_id); if (!ref) { @@ -151,14 +156,17 @@ const GodotRTCDataChannel = { return 65535; }, + godot_js_rtc_datachannel_max_retransmits_get__sig: 'ii', godot_js_rtc_datachannel_max_retransmits_get: function (p_id) { return IDHandler.get_prop(p_id, 'maxRetransmits', 65535); }, - godot_js_rtc_datachannel_is_negotiated: function (p_id, p_def) { + godot_js_rtc_datachannel_is_negotiated__sig: 'ii', + godot_js_rtc_datachannel_is_negotiated: function (p_id) { return IDHandler.get_prop(p_id, 'negotiated', 65535); }, + godot_js_rtc_datachannel_label_get__sig: 'ii', godot_js_rtc_datachannel_label_get: function (p_id) { const ref = IDHandler.get(p_id); if (!ref || !ref.label) { @@ -167,6 +175,7 @@ const GodotRTCDataChannel = { return GodotRuntime.allocString(ref.label); }, + godot_js_rtc_datachannel_protocol_get__sig: 'ii', godot_js_rtc_datachannel_protocol_get: function (p_id) { const ref = IDHandler.get(p_id); if (!ref || !ref.protocol) { @@ -175,11 +184,13 @@ const GodotRTCDataChannel = { return GodotRuntime.allocString(ref.protocol); }, + godot_js_rtc_datachannel_destroy__sig: 'vi', godot_js_rtc_datachannel_destroy: function (p_id) { GodotRTCDataChannel.close(p_id); IDHandler.remove(p_id); }, + godot_js_rtc_datachannel_connect__sig: 'viiiiii', godot_js_rtc_datachannel_connect: function (p_id, p_ref, p_on_open, p_on_message, p_on_error, p_on_close) { const onopen = GodotRuntime.get_func(p_on_open).bind(null, p_ref); const onmessage = GodotRuntime.get_func(p_on_message).bind(null, p_ref); @@ -188,6 +199,7 @@ const GodotRTCDataChannel = { GodotRTCDataChannel.connect(p_id, onopen, onmessage, onerror, onclose); }, + godot_js_rtc_datachannel_close__sig: 'vi', godot_js_rtc_datachannel_close: function (p_id) { const ref = IDHandler.get(p_id); if (!ref) { @@ -280,6 +292,7 @@ const GodotRTCPeerConnection = { }, }, + godot_js_rtc_pc_create__sig: 'iiiiii', godot_js_rtc_pc_create: function (p_config, p_ref, p_on_state_change, p_on_candidate, p_on_datachannel) { const onstatechange = GodotRuntime.get_func(p_on_state_change).bind(null, p_ref); const oncandidate = GodotRuntime.get_func(p_on_candidate).bind(null, p_ref); @@ -302,6 +315,7 @@ const GodotRTCPeerConnection = { return id; }, + godot_js_rtc_pc_close__sig: 'vi', godot_js_rtc_pc_close: function (p_id) { const ref = IDHandler.get(p_id); if (!ref) { @@ -310,6 +324,7 @@ const GodotRTCPeerConnection = { ref.close(); }, + godot_js_rtc_pc_destroy__sig: 'vi', godot_js_rtc_pc_destroy: function (p_id) { const ref = IDHandler.get(p_id); if (!ref) { @@ -321,6 +336,7 @@ const GodotRTCPeerConnection = { IDHandler.remove(p_id); }, + godot_js_rtc_pc_offer_create__sig: 'viiii', godot_js_rtc_pc_offer_create: function (p_id, p_obj, p_on_session, p_on_error) { const ref = IDHandler.get(p_id); if (!ref) { @@ -335,6 +351,7 @@ const GodotRTCPeerConnection = { }); }, + godot_js_rtc_pc_local_description_set__sig: 'viiiii', godot_js_rtc_pc_local_description_set: function (p_id, p_type, p_sdp, p_obj, p_on_error) { const ref = IDHandler.get(p_id); if (!ref) { @@ -351,6 +368,7 @@ const GodotRTCPeerConnection = { }); }, + godot_js_rtc_pc_remote_description_set__sig: 'viiiiii', godot_js_rtc_pc_remote_description_set: function (p_id, p_type, p_sdp, p_obj, p_session_created, p_on_error) { const ref = IDHandler.get(p_id); if (!ref) { @@ -375,6 +393,7 @@ const GodotRTCPeerConnection = { }); }, + godot_js_rtc_pc_ice_candidate_add__sig: 'viiii', godot_js_rtc_pc_ice_candidate_add: function (p_id, p_mid_name, p_mline_idx, p_sdp) { const ref = IDHandler.get(p_id); if (!ref) { @@ -390,6 +409,7 @@ const GodotRTCPeerConnection = { }, godot_js_rtc_pc_datachannel_create__deps: ['$GodotRTCDataChannel'], + godot_js_rtc_pc_datachannel_create__sig: 'iiii', godot_js_rtc_pc_datachannel_create: function (p_id, p_label, p_config) { try { const ref = IDHandler.get(p_id); diff --git a/modules/webrtc/register_types.cpp b/modules/webrtc/register_types.cpp index 0e830f0540..ecfaed9089 100644 --- a/modules/webrtc/register_types.cpp +++ b/modules/webrtc/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webrtc/register_types.h b/modules/webrtc/register_types.h index 8f5b9e8452..710ee88a28 100644 --- a/modules/webrtc/register_types.h +++ b/modules/webrtc/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webrtc/webrtc_data_channel.cpp b/modules/webrtc/webrtc_data_channel.cpp index cd9e77aff8..004112f992 100644 --- a/modules/webrtc/webrtc_data_channel.cpp +++ b/modules/webrtc/webrtc_data_channel.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webrtc/webrtc_data_channel.h b/modules/webrtc/webrtc_data_channel.h index 1407f1e3bd..20affc513f 100644 --- a/modules/webrtc/webrtc_data_channel.h +++ b/modules/webrtc/webrtc_data_channel.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webrtc/webrtc_data_channel_gdnative.cpp b/modules/webrtc/webrtc_data_channel_gdnative.cpp index 67ad2c07ce..d4cf464c7c 100644 --- a/modules/webrtc/webrtc_data_channel_gdnative.cpp +++ b/modules/webrtc/webrtc_data_channel_gdnative.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webrtc/webrtc_data_channel_gdnative.h b/modules/webrtc/webrtc_data_channel_gdnative.h index 03396d207d..7e02a32046 100644 --- a/modules/webrtc/webrtc_data_channel_gdnative.h +++ b/modules/webrtc/webrtc_data_channel_gdnative.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webrtc/webrtc_data_channel_js.cpp b/modules/webrtc/webrtc_data_channel_js.cpp index 3a63001a56..9f2b084cb1 100644 --- a/modules/webrtc/webrtc_data_channel_js.cpp +++ b/modules/webrtc/webrtc_data_channel_js.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webrtc/webrtc_data_channel_js.h b/modules/webrtc/webrtc_data_channel_js.h index e251760019..8c56b62303 100644 --- a/modules/webrtc/webrtc_data_channel_js.h +++ b/modules/webrtc/webrtc_data_channel_js.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webrtc/webrtc_multiplayer.cpp b/modules/webrtc/webrtc_multiplayer.cpp index e0c0cad68c..741cad5640 100644 --- a/modules/webrtc/webrtc_multiplayer.cpp +++ b/modules/webrtc/webrtc_multiplayer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webrtc/webrtc_multiplayer.h b/modules/webrtc/webrtc_multiplayer.h index fb37bd7722..6b4ae6fcc8 100644 --- a/modules/webrtc/webrtc_multiplayer.h +++ b/modules/webrtc/webrtc_multiplayer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webrtc/webrtc_peer_connection.cpp b/modules/webrtc/webrtc_peer_connection.cpp index 670924bca2..3e2938bf7d 100644 --- a/modules/webrtc/webrtc_peer_connection.cpp +++ b/modules/webrtc/webrtc_peer_connection.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webrtc/webrtc_peer_connection.h b/modules/webrtc/webrtc_peer_connection.h index 7366c3d0e8..ae75864489 100644 --- a/modules/webrtc/webrtc_peer_connection.h +++ b/modules/webrtc/webrtc_peer_connection.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webrtc/webrtc_peer_connection_gdnative.cpp b/modules/webrtc/webrtc_peer_connection_gdnative.cpp index aaa45d3a54..dcf78dfb73 100644 --- a/modules/webrtc/webrtc_peer_connection_gdnative.cpp +++ b/modules/webrtc/webrtc_peer_connection_gdnative.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webrtc/webrtc_peer_connection_gdnative.h b/modules/webrtc/webrtc_peer_connection_gdnative.h index 846b65c466..578af0202f 100644 --- a/modules/webrtc/webrtc_peer_connection_gdnative.h +++ b/modules/webrtc/webrtc_peer_connection_gdnative.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webrtc/webrtc_peer_connection_js.cpp b/modules/webrtc/webrtc_peer_connection_js.cpp index ad9b46a8af..8879f7d6ec 100644 --- a/modules/webrtc/webrtc_peer_connection_js.cpp +++ b/modules/webrtc/webrtc_peer_connection_js.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webrtc/webrtc_peer_connection_js.h b/modules/webrtc/webrtc_peer_connection_js.h index e33dd5f259..0272e67f6f 100644 --- a/modules/webrtc/webrtc_peer_connection_js.h +++ b/modules/webrtc/webrtc_peer_connection_js.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/websocket/SCsub b/modules/websocket/SCsub index 13e51a39c0..4c022c43cf 100644 --- a/modules/websocket/SCsub +++ b/modules/websocket/SCsub @@ -5,28 +5,44 @@ Import("env_modules") env_ws = env_modules.Clone() +thirdparty_obj = [] + if env["platform"] == "javascript": # Our JavaScript/C++ interface. env.AddJSLibraries(["library_godot_websocket.js"]) + elif env["builtin_wslay"]: # Thirdparty source files - wslay_dir = "#thirdparty/wslay/" - wslay_sources = [ + thirdparty_dir = "#thirdparty/wslay/" + thirdparty_sources = [ "wslay_net.c", "wslay_event.c", "wslay_queue.c", "wslay_stack.c", "wslay_frame.c", ] - wslay_sources = [wslay_dir + s for s in wslay_sources] - env_ws.Prepend(CPPPATH=[wslay_dir + "includes/"]) + thirdparty_sources = [thirdparty_dir + s for s in thirdparty_sources] + + env_ws.Prepend(CPPPATH=[thirdparty_dir + "includes/"]) env_ws.Append(CPPDEFINES=["HAVE_CONFIG_H"]) + if env["platform"] == "windows" or env["platform"] == "uwp": env_ws.Append(CPPDEFINES=["HAVE_WINSOCK2_H"]) else: env_ws.Append(CPPDEFINES=["HAVE_NETINET_IN_H"]) - env_wslay = env_ws.Clone() - env_wslay.disable_warnings() - env_wslay.add_source_files(env.modules_sources, wslay_sources) -env_ws.add_source_files(env.modules_sources, "*.cpp") + env_thirdparty = env_ws.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) + env.modules_sources += thirdparty_obj + + +# Godot source files + +module_obj = [] + +env_ws.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj + +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/websocket/editor_debugger_server_websocket.cpp b/modules/websocket/editor_debugger_server_websocket.cpp index 8eb975b323..b02d212c42 100644 --- a/modules/websocket/editor_debugger_server_websocket.cpp +++ b/modules/websocket/editor_debugger_server_websocket.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/websocket/editor_debugger_server_websocket.h b/modules/websocket/editor_debugger_server_websocket.h index 861f389aab..2f73b98c3d 100644 --- a/modules/websocket/editor_debugger_server_websocket.h +++ b/modules/websocket/editor_debugger_server_websocket.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/websocket/emws_client.cpp b/modules/websocket/emws_client.cpp index d6e00a26af..aec01a1eea 100644 --- a/modules/websocket/emws_client.cpp +++ b/modules/websocket/emws_client.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/websocket/emws_client.h b/modules/websocket/emws_client.h index 0123c37457..fdf7a231d2 100644 --- a/modules/websocket/emws_client.h +++ b/modules/websocket/emws_client.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/websocket/emws_peer.cpp b/modules/websocket/emws_peer.cpp index 5dcfba5567..496c1edc04 100644 --- a/modules/websocket/emws_peer.cpp +++ b/modules/websocket/emws_peer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/websocket/emws_peer.h b/modules/websocket/emws_peer.h index 2291a32bbc..07f61b62a0 100644 --- a/modules/websocket/emws_peer.h +++ b/modules/websocket/emws_peer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/websocket/emws_server.cpp b/modules/websocket/emws_server.cpp index 9d43283d3e..a35d84f372 100644 --- a/modules/websocket/emws_server.cpp +++ b/modules/websocket/emws_server.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/websocket/emws_server.h b/modules/websocket/emws_server.h index 1ce17855fe..4179b20ffe 100644 --- a/modules/websocket/emws_server.h +++ b/modules/websocket/emws_server.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/websocket/library_godot_websocket.js b/modules/websocket/library_godot_websocket.js index 6ada4e7335..b182d1ecde 100644 --- a/modules/websocket/library_godot_websocket.js +++ b/modules/websocket/library_godot_websocket.js @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -135,6 +135,7 @@ const GodotWebSocket = { }, }, + godot_js_websocket_create__sig: 'iiiiiiii', godot_js_websocket_create: function (p_ref, p_url, p_proto, p_on_open, p_on_message, p_on_error, p_on_close) { const on_open = GodotRuntime.get_func(p_on_open).bind(null, p_ref); const on_message = GodotRuntime.get_func(p_on_message).bind(null, p_ref); @@ -156,6 +157,7 @@ const GodotWebSocket = { return GodotWebSocket.create(socket, on_open, on_message, on_error, on_close); }, + godot_js_websocket_send__sig: 'iiiii', godot_js_websocket_send: function (p_id, p_buf, p_buf_len, p_raw) { const bytes_array = new Uint8Array(p_buf_len); let i = 0; @@ -169,12 +171,14 @@ const GodotWebSocket = { return GodotWebSocket.send(p_id, out); }, + godot_js_websocket_close__sig: 'viii', godot_js_websocket_close: function (p_id, p_code, p_reason) { const code = p_code; const reason = GodotRuntime.parseString(p_reason); GodotWebSocket.close(p_id, code, reason); }, + godot_js_websocket_destroy__sig: 'vi', godot_js_websocket_destroy: function (p_id) { GodotWebSocket.destroy(p_id); }, diff --git a/modules/websocket/packet_buffer.h b/modules/websocket/packet_buffer.h index 18b47b8d50..ed756363cf 100644 --- a/modules/websocket/packet_buffer.h +++ b/modules/websocket/packet_buffer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/websocket/register_types.cpp b/modules/websocket/register_types.cpp index 8979a09619..5a02509c4a 100644 --- a/modules/websocket/register_types.cpp +++ b/modules/websocket/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/websocket/register_types.h b/modules/websocket/register_types.h index bb7be57ab3..3884db67b7 100644 --- a/modules/websocket/register_types.h +++ b/modules/websocket/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/websocket/remote_debugger_peer_websocket.cpp b/modules/websocket/remote_debugger_peer_websocket.cpp index 9a72e460e2..c9591cc564 100644 --- a/modules/websocket/remote_debugger_peer_websocket.cpp +++ b/modules/websocket/remote_debugger_peer_websocket.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/websocket/remote_debugger_peer_websocket.h b/modules/websocket/remote_debugger_peer_websocket.h index bb03e5e892..03c60fb480 100644 --- a/modules/websocket/remote_debugger_peer_websocket.h +++ b/modules/websocket/remote_debugger_peer_websocket.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/websocket/websocket_client.cpp b/modules/websocket/websocket_client.cpp index 8feaa9af5a..eb0252e6d1 100644 --- a/modules/websocket/websocket_client.cpp +++ b/modules/websocket/websocket_client.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/websocket/websocket_client.h b/modules/websocket/websocket_client.h index 2966dc480b..78b77b89cd 100644 --- a/modules/websocket/websocket_client.h +++ b/modules/websocket/websocket_client.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/websocket/websocket_macros.h b/modules/websocket/websocket_macros.h index cf4545b435..d04909c97d 100644 --- a/modules/websocket/websocket_macros.h +++ b/modules/websocket/websocket_macros.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/websocket/websocket_multiplayer_peer.cpp b/modules/websocket/websocket_multiplayer_peer.cpp index fa2fe891a5..f94642475c 100644 --- a/modules/websocket/websocket_multiplayer_peer.cpp +++ b/modules/websocket/websocket_multiplayer_peer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/websocket/websocket_multiplayer_peer.h b/modules/websocket/websocket_multiplayer_peer.h index 54daae23a6..e593163b7c 100644 --- a/modules/websocket/websocket_multiplayer_peer.h +++ b/modules/websocket/websocket_multiplayer_peer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/websocket/websocket_peer.cpp b/modules/websocket/websocket_peer.cpp index 30a5972330..e77fdcfed2 100644 --- a/modules/websocket/websocket_peer.cpp +++ b/modules/websocket/websocket_peer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/websocket/websocket_peer.h b/modules/websocket/websocket_peer.h index 729fdfd340..2ba83637f9 100644 --- a/modules/websocket/websocket_peer.h +++ b/modules/websocket/websocket_peer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/websocket/websocket_server.cpp b/modules/websocket/websocket_server.cpp index b20b925dec..f57e8d959c 100644 --- a/modules/websocket/websocket_server.cpp +++ b/modules/websocket/websocket_server.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/websocket/websocket_server.h b/modules/websocket/websocket_server.h index 34ae52a1ee..3fbd5e3b95 100644 --- a/modules/websocket/websocket_server.h +++ b/modules/websocket/websocket_server.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/websocket/wsl_client.cpp b/modules/websocket/wsl_client.cpp index a2b81438df..3e2f48e9b3 100644 --- a/modules/websocket/wsl_client.cpp +++ b/modules/websocket/wsl_client.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/websocket/wsl_client.h b/modules/websocket/wsl_client.h index 0141ea93ea..8712b57f2c 100644 --- a/modules/websocket/wsl_client.h +++ b/modules/websocket/wsl_client.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/websocket/wsl_peer.cpp b/modules/websocket/wsl_peer.cpp index bf1ba43f8a..9d016e1139 100644 --- a/modules/websocket/wsl_peer.cpp +++ b/modules/websocket/wsl_peer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/websocket/wsl_peer.h b/modules/websocket/wsl_peer.h index 35ac18615a..01efa4b21e 100644 --- a/modules/websocket/wsl_peer.h +++ b/modules/websocket/wsl_peer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/websocket/wsl_server.cpp b/modules/websocket/wsl_server.cpp index 9a05967e4e..9df076bf3f 100644 --- a/modules/websocket/wsl_server.cpp +++ b/modules/websocket/wsl_server.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/websocket/wsl_server.h b/modules/websocket/wsl_server.h index f86de02797..8b2d4d3a04 100644 --- a/modules/websocket/wsl_server.h +++ b/modules/websocket/wsl_server.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webxr/SCsub b/modules/webxr/SCsub new file mode 100644 index 0000000000..0a96af0811 --- /dev/null +++ b/modules/webxr/SCsub @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +Import("env") +Import("env_modules") + +if env["platform"] == "javascript": + env.AddJSLibraries(["native/library_godot_webxr.js"]) + env.AddJSExterns(["native/webxr.externs.js"]) + +env_webxr = env_modules.Clone() +env_webxr.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/webxr/config.py b/modules/webxr/config.py new file mode 100644 index 0000000000..9efebed4e6 --- /dev/null +++ b/modules/webxr/config.py @@ -0,0 +1,14 @@ +def can_build(env, platform): + return True + + +def configure(env): + pass + + +def get_doc_classes(): + return ["WebXRInterface"] + + +def get_doc_path(): + return "doc_classes" diff --git a/modules/webxr/doc_classes/WebXRInterface.xml b/modules/webxr/doc_classes/WebXRInterface.xml new file mode 100644 index 0000000000..bddffd910e --- /dev/null +++ b/modules/webxr/doc_classes/WebXRInterface.xml @@ -0,0 +1,253 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="WebXRInterface" inherits="XRInterface" version="4.0"> + <brief_description> + AR/VR interface using WebXR. + </brief_description> + <description> + WebXR is an open standard that allows creating VR and AR applications that run in the web browser. + As such, this interface is only available when running in an HTML5 export. + WebXR supports a wide range of devices, from the very capable (like Valve Index, HTC Vive, Oculus Rift and Quest) down to the much less capable (like Google Cardboard, Oculus Go, GearVR, or plain smartphones). + Since WebXR is based on Javascript, it makes extensive use of callbacks, which means that [WebXRInterface] is forced to use signals, where other AR/VR interfaces would instead use functions that return a result immediately. This makes [WebXRInterface] quite a bit more complicated to intialize than other AR/VR interfaces. + Here's the minimum code required to start an immersive VR session: + [codeblock] + var webxr_interface + var vr_supported = false + + func _ready(): + # We assume this node has a canvas layer with a button on it as a child. + # This button is for the user to consent to entering immersive VR mode. + $CanvasLayer/Button.connect("pressed", self, "_on_Button_pressed") + + webxr_interface = XRServer.find_interface("WebXR") + if webxr_interface: + # WebXR uses a lot of asynchronous callbacks, so we connect to various + # signals in order to receive them. + webxr_interface.connect("session_supported", self, "_webxr_session_supported") + webxr_interface.connect("session_started", self, "_webxr_session_started") + webxr_interface.connect("session_ended", self, "_webxr_session_ended") + webxr_interface.connect("session_failed", self, "_webxr_session_failed") + + # This returns immediately - our _webxr_session_supported() method + # (which we connected to the "session_supported" signal above) will + # be called sometime later to let us know if it's supported or not. + webxr_interface.is_session_supported("immersive-vr") + + func _webxr_session_supported(session_mode, supported): + if session_mode == 'immersive-vr': + vr_supported = supported + + func _on_Button_pressed(): + if not vr_supported: + OS.alert("Your browser doesn't support VR") + return + + # We want an immersive VR session, as opposed to AR ('immersive-ar') or a + # simple 3DoF viewer ('viewer'). + webxr_interface.session_mode = 'immersive-vr' + # 'bounded-floor' is room scale, 'local-floor' is a standing or sitting + # experience (it puts you 1.6m above the ground if you have 3DoF headset), + # whereas as 'local' puts you down at the XROrigin. + # This list means it'll first try to request 'bounded-floor', then + # fallback on 'local-floor' and ultimately 'local', if nothing else is + # supported. + webxr_interface.requested_reference_space_types = 'bounded-floor, local-floor, local' + # In order to use 'local-floor' or 'bounded-floor' we must also + # mark the features as required or optional. + webxr_interface.required_features = 'local-floor' + webxr_interface.optional_features = 'bounded-floor' + + # This will return false if we're unable to even request the session, + # however, it can still fail asynchronously later in the process, so we + # only know if it's really succeeded or failed when our + # _webxr_session_started() or _webxr_session_failed() methods are called. + if not webxr_interface.initialize(): + OS.alert("Failed to initialize") + return + + func _webxr_session_started(): + # This tells Godot to start rendering to the headset. + get_viewport().xr = true + # This will be the reference space type you ultimately got, out of the + # types that you requested above. This is useful if you want the game to + # work a little differently in 'bounded-floor' versus 'local-floor'. + print ("Reference space type: " + webxr_interface.reference_space_type) + + func _webxr_session_ended(): + # If the user exits immersive mode, then we tell Godot to render to the web + # page again. + get_viewport().xr = false + + func _webxr_session_failed(message): + OS.alert("Failed to initialize: " + message) + [/codeblock] + There are several ways to handle "controller" input: + - Using [XRController3D] nodes and their [signal XRController3D.button_pressed] and [signal XRController3D.button_released] signals. This is how controllers are typically handled in AR/VR apps in Godot, however, this will only work with advanced VR controllers like the Oculus Touch or Index controllers, for example. The buttons codes are defined by [url=https://immersive-web.github.io/webxr-gamepads-module/#xr-standard-gamepad-mapping]Section 3.3 of the WebXR Gamepads Module[/url]. + - Using [method Node._unhandled_input] and [InputEventJoypadButton] or [InputEventJoypadMotion]. This works the same as normal joypads, except the [member InputEvent.device] starts at 100, so the left controller is 100 and the right controller is 101, and the button codes are also defined by [url=https://immersive-web.github.io/webxr-gamepads-module/#xr-standard-gamepad-mapping]Section 3.3 of the WebXR Gamepads Module[/url]. + - Using the [signal select], [signal squeeze] and related signals. This method will work for both advanced VR controllers, and non-traditional "controllers" like a tap on the screen, a spoken voice command or a button press on the device itself. The [code]controller_id[/code] passed to these signals is the same id as used in [member XRController3D.controller_id]. + You can use one or all of these methods to allow your game or app to support a wider or narrower set of devices and input methods, or to allow more advanced interations with more advanced devices. + </description> + <tutorials> + <link title="How to make a VR game for WebXR with Godot">https://www.snopekgames.com/blog/2020/how-make-vr-game-webxr-godot</link> + </tutorials> + <methods> + <method name="get_controller" qualifiers="const"> + <return type="XRPositionalTracker"> + </return> + <argument index="0" name="controller_id" type="int"> + </argument> + <description> + Gets an [XRPositionalTracker] for the given [code]controller_id[/code]. + In the context of WebXR, a "controller" can be an advanced VR controller like the Oculus Touch or Index controllers, or even a tap on the screen, a spoken voice command or a button press on the device itself. When a non-traditional controller is used, interpret the position and orientation of the [XRPositionalTracker] as a ray pointing at the object the user wishes to interact with. + Use this method to get information about the controller that triggered one of these signals: + - [signal selectstart] + - [signal select] + - [signal selectend] + - [signal squeezestart] + - [signal squeeze] + - [signal squeezestart] + </description> + </method> + <method name="is_session_supported"> + <return type="void"> + </return> + <argument index="0" name="session_mode" type="String"> + </argument> + <description> + Checks if the given [code]session_mode[/code] is supported by the user's browser. + Possible values come from [url=https://developer.mozilla.org/en-US/docs/Web/API/XRSessionMode]WebXR's XRSessionMode[/url], including: [code]"immersive-vr"[/code], [code]"immersive-ar"[/code], and [code]"inline"[/code]. + This method returns nothing, instead it emits the [signal session_supported] signal with the result. + </description> + </method> + </methods> + <members> + <member name="bounds_geometry" type="PackedVector3Array" setter="" getter="get_bounds_geometry"> + The vertices of a polygon which defines the boundaries of the user's play area. + This will only be available if [member reference_space_type] is [code]"bounded-floor"[/code] and only on certain browsers and devices that support it. + The [signal reference_space_reset] signal may indicate when this changes. + </member> + <member name="optional_features" type="String" setter="set_optional_features" getter="get_optional_features"> + A comma-seperated list of optional features used by [method XRInterface.initialize] when setting up the WebXR session. + If a user's browser or device doesn't support one of the given features, initialization will continue, but you won't be able to use the requested feature. + This doesn't have any effect on the interface when already initialized. + Possible values come from [url=https://developer.mozilla.org/en-US/docs/Web/API/XRReferenceSpaceType]WebXR's XRReferenceSpaceType[/url]. If you want to use a particular reference space type, it must be listed in either [member required_features] or [member optional_features]. + </member> + <member name="reference_space_type" type="String" setter="" getter="get_reference_space_type"> + The reference space type (from the list of requested types set in the [member requested_reference_space_types] property), that was ultimately used by [method XRInterface.initialize] when setting up the WebXR session. + Possible values come from [url=https://developer.mozilla.org/en-US/docs/Web/API/XRReferenceSpaceType]WebXR's XRReferenceSpaceType[/url]. If you want to use a particular reference space type, it must be listed in either [member required_features] or [member optional_features]. + </member> + <member name="requested_reference_space_types" type="String" setter="set_requested_reference_space_types" getter="get_requested_reference_space_types"> + A comma-seperated list of reference space types used by [method XRInterface.initialize] when setting up the WebXR session. + The reference space types are requested in order, and the first on supported by the users device or browser will be used. The [member reference_space_type] property contains the reference space type that was ultimately used. + This doesn't have any effect on the interface when already initialized. + Possible values come from [url=https://developer.mozilla.org/en-US/docs/Web/API/XRReferenceSpaceType]WebXR's XRReferenceSpaceType[/url]. If you want to use a particular reference space type, it must be listed in either [member required_features] or [member optional_features]. + </member> + <member name="required_features" type="String" setter="set_required_features" getter="get_required_features"> + A comma-seperated list of required features used by [method XRInterface.initialize] when setting up the WebXR session. + If a user's browser or device doesn't support one of the given features, initialization will fail and [signal session_failed] will be emitted. + This doesn't have any effect on the interface when already initialized. + Possible values come from [url=https://developer.mozilla.org/en-US/docs/Web/API/XRReferenceSpaceType]WebXR's XRReferenceSpaceType[/url]. If you want to use a particular reference space type, it must be listed in either [member required_features] or [member optional_features]. + </member> + <member name="session_mode" type="String" setter="set_session_mode" getter="get_session_mode"> + The session mode used by [method XRInterface.initialize] when setting up the WebXR session. + This doesn't have any effect on the interface when already initialized. + Possible values come from [url=https://developer.mozilla.org/en-US/docs/Web/API/XRSessionMode]WebXR's XRSessionMode[/url], including: [code]"immersive-vr"[/code], [code]"immersive-ar"[/code], and [code]"inline"[/code]. + </member> + <member name="visibility_state" type="String" setter="" getter="get_visibility_state"> + Indicates if the WebXR session's imagery is visible to the user. + Possible values come from [url=https://developer.mozilla.org/en-US/docs/Web/API/XRVisibilityState]WebXR's XRVisibilityState[/url], including [code]"hidden"[/code], [code]"visible"[/code], and [code]"visible-blurred"[/code]. + </member> + </members> + <signals> + <signal name="reference_space_reset"> + <description> + Emitted to indicate that the reference space has been reset or reconfigured. + When (or whether) this is emitted depends on the user's browser or device, but may include when the user has changed the dimensions of their play space (which you may be able to access via [member bounds_geometry]) or pressed/held a button to recenter their position. + See [url=https://developer.mozilla.org/en-US/docs/Web/API/XRReferenceSpace/reset_event]WebXR's XRReferenceSpace reset event[/url] for more information. + </description> + </signal> + <signal name="select"> + <argument index="0" name="controller_id" type="int"> + </argument> + <description> + Emitted after one of the "controllers" has finished its "primary action". + Use [method get_controller] to get more information about the controller. + </description> + </signal> + <signal name="selectend"> + <argument index="0" name="controller_id" type="int"> + </argument> + <description> + Emitted when one of the "controllers" has finished its "primary action". + Use [method get_controller] to get more information about the controller. + </description> + </signal> + <signal name="selectstart"> + <argument index="0" name="controller_id" type="int"> + </argument> + <description> + Emitted when one of the "controllers" has started its "primary action". + Use [method get_controller] to get more information about the controller. + </description> + </signal> + <signal name="session_ended"> + <description> + Emitted when the user ends the WebXR session (which can be done using UI from the browser or device). + At this point, you should do [code]get_viewport().xr = false[/code] to instruct Godot to resume rendering to the screen. + </description> + </signal> + <signal name="session_failed"> + <argument index="0" name="message" type="String"> + </argument> + <description> + Emitted by [method XRInterface.initialize] if the session fails to start. + [code]message[/code] may optionally contain an error message from WebXR, or an empty string if no message is available. + </description> + </signal> + <signal name="session_started"> + <description> + Emitted by [method XRInterface.initialize] if the session is successfully started. + At this point, it's safe to do [code]get_viewport().xr = true[/code] to instruct Godot to start rendering to the AR/VR device. + </description> + </signal> + <signal name="session_supported"> + <argument index="0" name="session_mode" type="String"> + </argument> + <argument index="1" name="supported" type="bool"> + </argument> + <description> + Emitted by [method is_session_supported] to indicate if the given [code]session_mode[/code] is supported or not. + </description> + </signal> + <signal name="squeeze"> + <argument index="0" name="controller_id" type="int"> + </argument> + <description> + Emitted after one of the "controllers" has finished its "primary squeeze action". + Use [method get_controller] to get more information about the controller. + </description> + </signal> + <signal name="squeezeend"> + <argument index="0" name="controller_id" type="int"> + </argument> + <description> + Emitted when one of the "controllers" has finished its "primary squeeze action". + Use [method get_controller] to get more information about the controller. + </description> + </signal> + <signal name="squeezestart"> + <argument index="0" name="controller_id" type="int"> + </argument> + <description> + Emitted when one of the "controllers" has started its "primary squeeze action". + Use [method get_controller] to get more information about the controller. + </description> + </signal> + <signal name="visibility_state_changed"> + <description> + Emitted when [member visibility_state] has changed. + </description> + </signal> + </signals> + <constants> + </constants> +</class> diff --git a/modules/webxr/godot_webxr.h b/modules/webxr/godot_webxr.h new file mode 100644 index 0000000000..5e50ffde28 --- /dev/null +++ b/modules/webxr/godot_webxr.h @@ -0,0 +1,84 @@ +/*************************************************************************/ +/* godot_webxr.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef GODOT_WEBXR_H +#define GODOT_WEBXR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "stddef.h" + +typedef void (*GodotWebXRSupportedCallback)(char *p_session_mode, int p_supported); +typedef void (*GodotWebXRStartedCallback)(char *p_reference_space_type); +typedef void (*GodotWebXREndedCallback)(); +typedef void (*GodotWebXRFailedCallback)(char *p_message); +typedef void (*GodotWebXRControllerCallback)(); +typedef void (*GodotWebXRInputEventCallback)(char *p_signal_name, int p_controller_id); +typedef void (*GodotWebXRSimpleEventCallback)(char *p_signal_name); + +extern int godot_webxr_is_supported(); +extern void godot_webxr_is_session_supported(const char *p_session_mode, GodotWebXRSupportedCallback p_callback); + +extern void godot_webxr_initialize( + const char *p_session_mode, + const char *p_required_features, + const char *p_optional_features, + const char *p_requested_reference_space_types, + GodotWebXRStartedCallback p_on_session_started, + GodotWebXREndedCallback p_on_session_ended, + GodotWebXRFailedCallback p_on_session_failed, + GodotWebXRControllerCallback p_on_controller_changed, + GodotWebXRInputEventCallback p_on_input_event, + GodotWebXRSimpleEventCallback p_on_simple_event); +extern void godot_webxr_uninitialize(); + +extern int *godot_webxr_get_render_targetsize(); +extern float *godot_webxr_get_transform_for_eye(int p_eye); +extern float *godot_webxr_get_projection_for_eye(int p_eye); +extern int godot_webxr_get_external_texture_for_eye(int p_eye); +extern void godot_webxr_commit_for_eye(int p_eye); + +extern void godot_webxr_sample_controller_data(); +extern int godot_webxr_get_controller_count(); +extern int godot_webxr_is_controller_connected(int p_controller); +extern float *godot_webxr_get_controller_transform(int p_controller); +extern int *godot_webxr_get_controller_buttons(int p_controller); +extern int *godot_webxr_get_controller_axes(int p_controller); + +extern char *godot_webxr_get_visibility_state(); +extern int *godot_webxr_get_bounds_geometry(); + +#ifdef __cplusplus +} +#endif + +#endif /* GODOT_WEBXR_H */ diff --git a/modules/webxr/native/library_godot_webxr.js b/modules/webxr/native/library_godot_webxr.js new file mode 100644 index 0000000000..3041c16c79 --- /dev/null +++ b/modules/webxr/native/library_godot_webxr.js @@ -0,0 +1,651 @@ +/*************************************************************************/ +/* library_godot_webxr.js */ +/*************************************************************************/ +/* 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. */ +/*************************************************************************/ +const GodotWebXR = { + $GodotWebXR__deps: ['$Browser', '$GL', '$GodotRuntime'], + $GodotWebXR: { + gl: null, + + texture_ids: [null, null], + textures: [null, null], + + session: null, + space: null, + frame: null, + pose: null, + + // Monkey-patch the requestAnimationFrame() used by Emscripten for the main + // loop, so that we can swap it out for XRSession.requestAnimationFrame() + // when an XR session is started. + orig_requestAnimationFrame: null, + requestAnimationFrame: (callback) => { + if (GodotWebXR.session && GodotWebXR.space) { + const onFrame = function (time, frame) { + GodotWebXR.frame = frame; + GodotWebXR.pose = frame.getViewerPose(GodotWebXR.space); + callback(time); + GodotWebXR.frame = null; + GodotWebXR.pose = null; + }; + GodotWebXR.session.requestAnimationFrame(onFrame); + } else { + GodotWebXR.orig_requestAnimationFrame(callback); + } + }, + monkeyPatchRequestAnimationFrame: (enable) => { + if (GodotWebXR.orig_requestAnimationFrame === null) { + GodotWebXR.orig_requestAnimationFrame = Browser.requestAnimationFrame; + } + Browser.requestAnimationFrame = enable + ? GodotWebXR.requestAnimationFrame : GodotWebXR.orig_requestAnimationFrame; + }, + pauseResumeMainLoop: () => { + // Once both GodotWebXR.session and GodotWebXR.space are set or + // unset, our monkey-patched requestAnimationFrame() should be + // enabled or disabled. When using the WebXR API Emulator, this + // gets picked up automatically, however, in the Oculus Browser + // on the Quest, we need to pause and resume the main loop. + Browser.pauseAsyncCallbacks(); + Browser.mainLoop.pause(); + window.setTimeout(function () { + Browser.resumeAsyncCallbacks(); + Browser.mainLoop.resume(); + }, 0); + }, + + // Some custom WebGL code for blitting our eye textures to the + // framebuffer we get from WebXR. + shaderProgram: null, + programInfo: null, + buffer: null, + // Vertex shader source. + vsSource: ` + const vec2 scale = vec2(0.5, 0.5); + attribute vec4 aVertexPosition; + + varying highp vec2 vTextureCoord; + + void main () { + gl_Position = aVertexPosition; + vTextureCoord = aVertexPosition.xy * scale + scale; + } + `, + // Fragment shader source. + fsSource: ` + varying highp vec2 vTextureCoord; + + uniform sampler2D uSampler; + + void main() { + gl_FragColor = texture2D(uSampler, vTextureCoord); + } + `, + + initShaderProgram: (gl, vsSource, fsSource) => { + const vertexShader = GodotWebXR.loadShader(gl, gl.VERTEX_SHADER, vsSource); + const fragmentShader = GodotWebXR.loadShader(gl, gl.FRAGMENT_SHADER, fsSource); + + const shaderProgram = gl.createProgram(); + gl.attachShader(shaderProgram, vertexShader); + gl.attachShader(shaderProgram, fragmentShader); + gl.linkProgram(shaderProgram); + + if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { + GodotRuntime.error(`Unable to initialize the shader program: ${gl.getProgramInfoLog(shaderProgram)}`); + return null; + } + + return shaderProgram; + }, + loadShader: (gl, type, source) => { + const shader = gl.createShader(type); + gl.shaderSource(shader, source); + gl.compileShader(shader); + + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + GodotRuntime.error(`An error occurred compiling the shader: ${gl.getShaderInfoLog(shader)}`); + gl.deleteShader(shader); + return null; + } + + return shader; + }, + initBuffer: (gl) => { + const positionBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); + const positions = [ + -1.0, -1.0, + 1.0, -1.0, + -1.0, 1.0, + 1.0, 1.0, + ]; + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW); + return positionBuffer; + }, + blitTexture: (gl, texture) => { + if (GodotWebXR.shaderProgram === null) { + GodotWebXR.shaderProgram = GodotWebXR.initShaderProgram(gl, GodotWebXR.vsSource, GodotWebXR.fsSource); + GodotWebXR.programInfo = { + program: GodotWebXR.shaderProgram, + attribLocations: { + vertexPosition: gl.getAttribLocation(GodotWebXR.shaderProgram, 'aVertexPosition'), + }, + uniformLocations: { + uSampler: gl.getUniformLocation(GodotWebXR.shaderProgram, 'uSampler'), + }, + }; + GodotWebXR.buffer = GodotWebXR.initBuffer(gl); + } + + const orig_program = gl.getParameter(gl.CURRENT_PROGRAM); + gl.useProgram(GodotWebXR.shaderProgram); + + gl.bindBuffer(gl.ARRAY_BUFFER, GodotWebXR.buffer); + gl.vertexAttribPointer(GodotWebXR.programInfo.attribLocations.vertexPosition, 2, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(GodotWebXR.programInfo.attribLocations.vertexPosition); + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.uniform1i(GodotWebXR.programInfo.uniformLocations.uSampler, 0); + + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); + + // Restore state. + gl.bindTexture(gl.TEXTURE_2D, null); + gl.disableVertexAttribArray(GodotWebXR.programInfo.attribLocations.vertexPosition); + gl.bindBuffer(gl.ARRAY_BUFFER, null); + gl.useProgram(orig_program); + }, + + // Holds the controllers list between function calls. + controllers: [], + + // Updates controllers array, where the left hand (or sole tracker) is + // the first element, and the right hand is the second element, and any + // others placed at the 3rd position and up. + sampleControllers: () => { + if (!GodotWebXR.session || !GodotWebXR.frame) { + return; + } + + let other_index = 2; + const controllers = []; + GodotWebXR.session.inputSources.forEach((input_source) => { + if (input_source.targetRayMode === 'tracked-pointer') { + if (input_source.handedness === 'right') { + controllers[1] = input_source; + } else if (input_source.handedness === 'left' || !controllers[0]) { + controllers[0] = input_source; + } + } else { + controllers[other_index++] = input_source; + } + }); + GodotWebXR.controllers = controllers; + }, + + getControllerId: (input_source) => GodotWebXR.controllers.indexOf(input_source), + }, + + godot_webxr_is_supported__proxy: 'sync', + godot_webxr_is_supported__sig: 'i', + godot_webxr_is_supported: function () { + return !!navigator.xr; + }, + + godot_webxr_is_session_supported__proxy: 'sync', + godot_webxr_is_session_supported__sig: 'vii', + godot_webxr_is_session_supported: function (p_session_mode, p_callback) { + const session_mode = GodotRuntime.parseString(p_session_mode); + const cb = GodotRuntime.get_func(p_callback); + if (navigator.xr) { + navigator.xr.isSessionSupported(session_mode).then(function (supported) { + const c_str = GodotRuntime.allocString(session_mode); + cb(c_str, supported ? 1 : 0); + GodotRuntime.free(c_str); + }); + } else { + const c_str = GodotRuntime.allocString(session_mode); + cb(c_str, 0); + GodotRuntime.free(c_str); + } + }, + + godot_webxr_initialize__deps: ['emscripten_webgl_get_current_context'], + godot_webxr_initialize__proxy: 'sync', + godot_webxr_initialize__sig: 'viiiiiiiiii', + godot_webxr_initialize: function (p_session_mode, p_required_features, p_optional_features, p_requested_reference_spaces, p_on_session_started, p_on_session_ended, p_on_session_failed, p_on_controller_changed, p_on_input_event, p_on_simple_event) { + GodotWebXR.monkeyPatchRequestAnimationFrame(true); + + const session_mode = GodotRuntime.parseString(p_session_mode); + const required_features = GodotRuntime.parseString(p_required_features).split(',').map((s) => s.trim()).filter((s) => s !== ''); + const optional_features = GodotRuntime.parseString(p_optional_features).split(',').map((s) => s.trim()).filter((s) => s !== ''); + const requested_reference_space_types = GodotRuntime.parseString(p_requested_reference_spaces).split(',').map((s) => s.trim()); + const onstarted = GodotRuntime.get_func(p_on_session_started); + const onended = GodotRuntime.get_func(p_on_session_ended); + const onfailed = GodotRuntime.get_func(p_on_session_failed); + const oncontroller = GodotRuntime.get_func(p_on_controller_changed); + const oninputevent = GodotRuntime.get_func(p_on_input_event); + const onsimpleevent = GodotRuntime.get_func(p_on_simple_event); + + const session_init = {}; + if (required_features.length > 0) { + session_init['requiredFeatures'] = required_features; + } + if (optional_features.length > 0) { + session_init['optionalFeatures'] = optional_features; + } + + navigator.xr.requestSession(session_mode, session_init).then(function (session) { + GodotWebXR.session = session; + + session.addEventListener('end', function (evt) { + onended(); + }); + + session.addEventListener('inputsourceschange', function (evt) { + let controller_changed = false; + [evt.added, evt.removed].forEach((lst) => { + lst.forEach((input_source) => { + if (input_source.targetRayMode === 'tracked-pointer') { + controller_changed = true; + } + }); + }); + if (controller_changed) { + oncontroller(); + } + }); + + ['selectstart', 'select', 'selectend', 'squeezestart', 'squeeze', 'squeezeend'].forEach((input_event) => { + session.addEventListener(input_event, function (evt) { + const c_str = GodotRuntime.allocString(input_event); + oninputevent(c_str, GodotWebXR.getControllerId(evt.inputSource)); + GodotRuntime.free(c_str); + }); + }); + + session.addEventListener('visibilitychange', function (evt) { + const c_str = GodotRuntime.allocString('visibility_state_changed'); + onsimpleevent(c_str); + GodotRuntime.free(c_str); + }); + + const gl_context_handle = _emscripten_webgl_get_current_context(); // eslint-disable-line no-undef + const gl = GL.getContext(gl_context_handle).GLctx; + GodotWebXR.gl = gl; + + gl.makeXRCompatible().then(function () { + session.updateRenderState({ + baseLayer: new XRWebGLLayer(session, gl), + }); + + function onReferenceSpaceSuccess(reference_space, reference_space_type) { + GodotWebXR.space = reference_space; + + // Using reference_space.addEventListener() crashes when + // using the polyfill with the WebXR Emulator extension, + // so we set the event property instead. + reference_space.onreset = function (evt) { + const c_str = GodotRuntime.allocString('reference_space_reset'); + onsimpleevent(c_str); + GodotRuntime.free(c_str); + }; + + // Now that both GodotWebXR.session and GodotWebXR.space are + // set, we need to pause and resume the main loop for the XR + // main loop to kick in. + GodotWebXR.pauseResumeMainLoop(); + + // Call in setTimeout() so that errors in the onstarted() + // callback don't bubble up here and cause Godot to try the + // next reference space. + window.setTimeout(function () { + const c_str = GodotRuntime.allocString(reference_space_type); + onstarted(c_str); + GodotRuntime.free(c_str); + }, 0); + } + + function requestReferenceSpace() { + const reference_space_type = requested_reference_space_types.shift(); + session.requestReferenceSpace(reference_space_type) + .then((refSpace) => { + onReferenceSpaceSuccess(refSpace, reference_space_type); + }) + .catch(() => { + if (requested_reference_space_types.length === 0) { + const c_str = GodotRuntime.allocString('Unable to get any of the requested reference space types'); + onfailed(c_str); + GodotRuntime.free(c_str); + } else { + requestReferenceSpace(); + } + }); + } + + requestReferenceSpace(); + }).catch(function (error) { + const c_str = GodotRuntime.allocString(`Unable to make WebGL context compatible with WebXR: ${error}`); + onfailed(c_str); + GodotRuntime.free(c_str); + }); + }).catch(function (error) { + const c_str = GodotRuntime.allocString(`Unable to start session: ${error}`); + onfailed(c_str); + GodotRuntime.free(c_str); + }); + }, + + godot_webxr_uninitialize__proxy: 'sync', + godot_webxr_uninitialize__sig: 'v', + godot_webxr_uninitialize: function () { + if (GodotWebXR.session) { + GodotWebXR.session.end() + // Prevent exception when session has already ended. + .catch((e) => { }); + } + + // Clean-up the textures we allocated for each view. + const gl = GodotWebXR.gl; + for (let i = 0; i < GodotWebXR.textures.length; i++) { + const texture = GodotWebXR.textures[i]; + if (texture !== null) { + gl.deleteTexture(texture); + } + GodotWebXR.textures[i] = null; + GodotWebXR.texture_ids[i] = null; + } + + GodotWebXR.session = null; + GodotWebXR.space = null; + GodotWebXR.frame = null; + GodotWebXR.pose = null; + + // Disable the monkey-patched window.requestAnimationFrame() and + // pause/restart the main loop to activate it on all platforms. + GodotWebXR.monkeyPatchRequestAnimationFrame(false); + GodotWebXR.pauseResumeMainLoop(); + }, + + godot_webxr_get_render_targetsize__proxy: 'sync', + godot_webxr_get_render_targetsize__sig: 'i', + godot_webxr_get_render_targetsize: function () { + if (!GodotWebXR.session || !GodotWebXR.pose) { + return 0; + } + + const glLayer = GodotWebXR.session.renderState.baseLayer; + const view = GodotWebXR.pose.views[0]; + const viewport = glLayer.getViewport(view); + + const buf = GodotRuntime.malloc(2 * 4); + GodotRuntime.setHeapValue(buf + 0, viewport.width, 'i32'); + GodotRuntime.setHeapValue(buf + 4, viewport.height, 'i32'); + return buf; + }, + + godot_webxr_get_transform_for_eye__proxy: 'sync', + godot_webxr_get_transform_for_eye__sig: 'ii', + godot_webxr_get_transform_for_eye: function (p_eye) { + if (!GodotWebXR.session || !GodotWebXR.pose) { + return 0; + } + + const views = GodotWebXR.pose.views; + let matrix; + if (p_eye === 0) { + matrix = GodotWebXR.pose.transform.matrix; + } else { + matrix = views[p_eye - 1].transform.matrix; + } + const buf = GodotRuntime.malloc(16 * 4); + for (let i = 0; i < 16; i++) { + GodotRuntime.setHeapValue(buf + (i * 4), matrix[i], 'float'); + } + return buf; + }, + + godot_webxr_get_projection_for_eye__proxy: 'sync', + godot_webxr_get_projection_for_eye__sig: 'ii', + godot_webxr_get_projection_for_eye: function (p_eye) { + if (!GodotWebXR.session || !GodotWebXR.pose) { + return 0; + } + + const view_index = (p_eye === 2 /* ARVRInterface::EYE_RIGHT */) ? 1 : 0; + const matrix = GodotWebXR.pose.views[view_index].projectionMatrix; + const buf = GodotRuntime.malloc(16 * 4); + for (let i = 0; i < 16; i++) { + GodotRuntime.setHeapValue(buf + (i * 4), matrix[i], 'float'); + } + return buf; + }, + + godot_webxr_get_external_texture_for_eye__proxy: 'sync', + godot_webxr_get_external_texture_for_eye__sig: 'ii', + godot_webxr_get_external_texture_for_eye: function (p_eye) { + if (!GodotWebXR.session || !GodotWebXR.pose) { + return 0; + } + + const view_index = (p_eye === 2 /* ARVRInterface::EYE_RIGHT */) ? 1 : 0; + if (GodotWebXR.texture_ids[view_index]) { + return GodotWebXR.texture_ids[view_index]; + } + + const glLayer = GodotWebXR.session.renderState.baseLayer; + const view = GodotWebXR.pose.views[view_index]; + const viewport = glLayer.getViewport(view); + const gl = GodotWebXR.gl; + + const texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, viewport.width, viewport.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.bindTexture(gl.TEXTURE_2D, null); + + const texture_id = GL.getNewId(GL.textures); + GL.textures[texture_id] = texture; + GodotWebXR.textures[view_index] = texture; + GodotWebXR.texture_ids[view_index] = texture_id; + return texture_id; + }, + + godot_webxr_commit_for_eye__proxy: 'sync', + godot_webxr_commit_for_eye__sig: 'vi', + godot_webxr_commit_for_eye: function (p_eye) { + if (!GodotWebXR.session || !GodotWebXR.pose) { + return; + } + + const view_index = (p_eye === 2 /* ARVRInterface::EYE_RIGHT */) ? 1 : 0; + const glLayer = GodotWebXR.session.renderState.baseLayer; + const view = GodotWebXR.pose.views[view_index]; + const viewport = glLayer.getViewport(view); + const gl = GodotWebXR.gl; + + const orig_framebuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING); + const orig_viewport = gl.getParameter(gl.VIEWPORT); + + // Bind to WebXR's framebuffer. + gl.bindFramebuffer(gl.FRAMEBUFFER, glLayer.framebuffer); + gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height); + + GodotWebXR.blitTexture(gl, GodotWebXR.textures[view_index]); + + // Restore state. + gl.bindFramebuffer(gl.FRAMEBUFFER, orig_framebuffer); + gl.viewport(orig_viewport[0], orig_viewport[1], orig_viewport[2], orig_viewport[3]); + }, + + godot_webxr_sample_controller_data__proxy: 'sync', + godot_webxr_sample_controller_data__sig: 'v', + godot_webxr_sample_controller_data: function () { + GodotWebXR.sampleControllers(); + }, + + godot_webxr_get_controller_count__proxy: 'sync', + godot_webxr_get_controller_count__sig: 'i', + godot_webxr_get_controller_count: function () { + if (!GodotWebXR.session || !GodotWebXR.frame) { + return 0; + } + return GodotWebXR.controllers.length; + }, + + godot_webxr_is_controller_connected__proxy: 'sync', + godot_webxr_is_controller_connected__sig: 'ii', + godot_webxr_is_controller_connected: function (p_controller) { + if (!GodotWebXR.session || !GodotWebXR.frame) { + return false; + } + return !!GodotWebXR.controllers[p_controller]; + }, + + godot_webxr_get_controller_transform__proxy: 'sync', + godot_webxr_get_controller_transform__sig: 'ii', + godot_webxr_get_controller_transform: function (p_controller) { + if (!GodotWebXR.session || !GodotWebXR.frame) { + return 0; + } + + const controller = GodotWebXR.controllers[p_controller]; + if (!controller) { + return 0; + } + + const frame = GodotWebXR.frame; + const space = GodotWebXR.space; + + const pose = frame.getPose(controller.targetRaySpace, space); + if (!pose) { + // This can mean that the controller lost tracking. + return 0; + } + const matrix = pose.transform.matrix; + + const buf = GodotRuntime.malloc(16 * 4); + for (let i = 0; i < 16; i++) { + GodotRuntime.setHeapValue(buf + (i * 4), matrix[i], 'float'); + } + return buf; + }, + + godot_webxr_get_controller_buttons__proxy: 'sync', + godot_webxr_get_controller_buttons__sig: 'ii', + godot_webxr_get_controller_buttons: function (p_controller) { + if (GodotWebXR.controllers.length === 0) { + return 0; + } + + const controller = GodotWebXR.controllers[p_controller]; + if (!controller || !controller.gamepad) { + return 0; + } + + const button_count = controller.gamepad.buttons.length; + + const buf = GodotRuntime.malloc((button_count + 1) * 4); + GodotRuntime.setHeapValue(buf, button_count, 'i32'); + for (let i = 0; i < button_count; i++) { + GodotRuntime.setHeapValue(buf + 4 + (i * 4), controller.gamepad.buttons[i].value, 'float'); + } + return buf; + }, + + godot_webxr_get_controller_axes__proxy: 'sync', + godot_webxr_get_controller_axes__sig: 'ii', + godot_webxr_get_controller_axes: function (p_controller) { + if (GodotWebXR.controllers.length === 0) { + return 0; + } + + const controller = GodotWebXR.controllers[p_controller]; + if (!controller || !controller.gamepad) { + return 0; + } + + const axes_count = controller.gamepad.axes.length; + + const buf = GodotRuntime.malloc((axes_count + 1) * 4); + GodotRuntime.setHeapValue(buf, axes_count, 'i32'); + for (let i = 0; i < axes_count; i++) { + let value = controller.gamepad.axes[i]; + if (i === 1 || i === 3) { + // Invert the Y-axis on thumbsticks and trackpads, in order to + // match OpenXR and other XR platform SDKs. + value *= -1.0; + } + GodotRuntime.setHeapValue(buf + 4 + (i * 4), value, 'float'); + } + return buf; + }, + + godot_webxr_get_visibility_state__proxy: 'sync', + godot_webxr_get_visibility_state__sig: 'i', + godot_webxr_get_visibility_state: function () { + if (!GodotWebXR.session || !GodotWebXR.session.visibilityState) { + return 0; + } + + return GodotRuntime.allocString(GodotWebXR.session.visibilityState); + }, + + godot_webxr_get_bounds_geometry__proxy: 'sync', + godot_webxr_get_bounds_geometry__sig: 'i', + godot_webxr_get_bounds_geometry: function () { + if (!GodotWebXR.space || !GodotWebXR.space.boundsGeometry) { + return 0; + } + + const point_count = GodotWebXR.space.boundsGeometry.length; + if (point_count === 0) { + return 0; + } + + const buf = GodotRuntime.malloc(((point_count * 3) + 1) * 4); + GodotRuntime.setHeapValue(buf, point_count, 'i32'); + for (let i = 0; i < point_count; i++) { + const point = GodotWebXR.space.boundsGeometry[i]; + GodotRuntime.setHeapValue(buf + ((i * 3) + 1) * 4, point.x, 'float'); + GodotRuntime.setHeapValue(buf + ((i * 3) + 2) * 4, point.y, 'float'); + GodotRuntime.setHeapValue(buf + ((i * 3) + 3) * 4, point.z, 'float'); + } + + return buf; + }, +}; + +autoAddDeps(GodotWebXR, '$GodotWebXR'); +mergeInto(LibraryManager.library, GodotWebXR); diff --git a/modules/webxr/native/webxr.externs.js b/modules/webxr/native/webxr.externs.js new file mode 100644 index 0000000000..03dc05bc83 --- /dev/null +++ b/modules/webxr/native/webxr.externs.js @@ -0,0 +1,499 @@ +/** + * @type {XR} + */ +Navigator.prototype.xr; + +/** + * @constructor + */ +function XRSessionInit() {}; + +/** + * @type {Array<string>} + */ +XRSessionInit.prototype.requiredFeatures; + +/** + * @type {Array<string>} + */ +XRSessionInit.prototype.optionalFeatures; + +/** + * @constructor + */ +function XR() {} + +/** + * @type {?function (Event)} + */ +XR.prototype.ondevicechanged; + +/** + * @param {string} mode + * + * @return {!Promise<boolean>} + */ +XR.prototype.isSessionSupported = function(mode) {} + +/** + * @param {string} mode + * @param {XRSessionInit} options + * + * @return {!Promise<XRSession>} + */ +XR.prototype.requestSession = function(mode, options) {} + +/** + * @constructor + */ +function XRSession() {} + +/** + * @type {XRRenderState} + */ +XRSession.prototype.renderState; + +/** + * @type {Array<XRInputSource>} + */ +XRSession.prototype.inputSources; + +/** + * @type {string} + */ +XRSession.prototype.visibilityState; + +/** + * @type {?function (Event)} + */ +XRSession.prototype.onend; + +/** + * @type {?function (XRInputSourcesChangeEvent)} + */ +XRSession.prototype.oninputsourceschange; + +/** + * @type {?function (XRInputSourceEvent)} + */ +XRSession.prototype.onselectstart; + +/** + * @type {?function (XRInputSourceEvent)} + */ +XRSession.prototype.onselect; + +/** + * @type {?function (XRInputSourceEvent)} + */ +XRSession.prototype.onselectend; + +/** + * @type {?function (XRInputSourceEvent)} + */ +XRSession.prototype.onsqueezestart; + +/** + * @type {?function (XRInputSourceEvent)} + */ +XRSession.prototype.onsqueeze; + +/** + * @type {?function (XRInputSourceEvent)} + */ +XRSession.prototype.onsqueezeend; + +/** + * @type {?function (Event)} + */ +XRSession.prototype.onvisibilitychange; + +/** + * @param {XRRenderStateInit} state + * @return {void} + */ +XRSession.prototype.updateRenderState = function (state) {}; + +/** + * @param {XRFrameRequestCallback} callback + * @return {number} + */ +XRSession.prototype.requestAnimationFrame = function (callback) {}; + +/** + * @param {number} handle + * @return {void} + */ +XRSession.prototype.cancelAnimationFrame = function (handle) {}; + +/** + * @return {Promise<void>} + */ +XRSession.prototype.end = function () {}; + +/** + * @param {string} referenceSpaceType + * @return {Promise<XRReferenceSpace>} + */ +XRSession.prototype.requestReferenceSpace = function (referenceSpaceType) {}; + +/** + * @typedef {function(number, XRFrame): undefined} + */ +var XRFrameRequestCallback; + +/** + * @constructor + */ +function XRRenderStateInit() {} + +/** + * @type {number} + */ +XRRenderStateInit.prototype.depthNear; + +/** + * @type {number} + */ +XRRenderStateInit.prototype.depthFar; + +/** + * @type {number} + */ +XRRenderStateInit.prototype.inlineVerticalFieldOfView; + +/** + * @type {?XRWebGLLayer} + */ +XRRenderStateInit.prototype.baseLayer; + +/** + * @constructor + */ +function XRRenderState() {}; + +/** + * @type {number} + */ +XRRenderState.prototype.depthNear; + +/** + * @type {number} + */ +XRRenderState.prototype.depthFar; + +/** + * @type {?number} + */ +XRRenderState.prototype.inlineVerticalFieldOfView; + +/** + * @type {?XRWebGLLayer} + */ +XRRenderState.prototype.baseLayer; + +/** + * @constructor + */ +function XRFrame() {} + +/** + * @type {XRSession} + */ +XRFrame.prototype.session; + +/** + * @param {XRReferenceSpace} referenceSpace + * @return {?XRViewerPose} + */ +XRFrame.prototype.getViewerPose = function (referenceSpace) {}; + +/** + * + * @param {XRSpace} space + * @param {XRSpace} baseSpace + * @return {XRPose} + */ +XRFrame.prototype.getPose = function (space, baseSpace) {}; + +/** + * @constructor + */ +function XRReferenceSpace() {}; + +/** + * @type {Array<DOMPointReadOnly>} + */ +XRReferenceSpace.prototype.boundsGeometry; + +/** + * @param {XRRigidTransform} originOffset + * @return {XRReferenceSpace} + */ +XRReferenceSpace.prototype.getOffsetReferenceSpace = function(originOffset) {}; + +/** + * @type {?function (Event)} + */ +XRReferenceSpace.prototype.onreset; + +/** + * @constructor + */ +function XRRigidTransform() {}; + +/** + * @type {DOMPointReadOnly} + */ +XRRigidTransform.prototype.position; + +/** + * @type {DOMPointReadOnly} + */ +XRRigidTransform.prototype.orientation; + +/** + * @type {Float32Array} + */ +XRRigidTransform.prototype.matrix; + +/** + * @type {XRRigidTransform} + */ +XRRigidTransform.prototype.inverse; + +/** + * @constructor + */ +function XRView() {} + +/** + * @type {string} + */ +XRView.prototype.eye; + +/** + * @type {Float32Array} + */ +XRView.prototype.projectionMatrix; + +/** + * @type {XRRigidTransform} + */ +XRView.prototype.transform; + +/** + * @constructor + */ +function XRViewerPose() {} + +/** + * @type {Array<XRView>} + */ +XRViewerPose.prototype.views; + +/** + * @constructor + */ +function XRViewport() {} + +/** + * @type {number} + */ +XRViewport.prototype.x; + +/** + * @type {number} + */ +XRViewport.prototype.y; + +/** + * @type {number} + */ +XRViewport.prototype.width; + +/** + * @type {number} + */ +XRViewport.prototype.height; + +/** + * @constructor + */ +function XRWebGLLayerInit() {}; + +/** + * @type {boolean} + */ +XRWebGLLayerInit.prototype.antialias; + +/** + * @type {boolean} + */ +XRWebGLLayerInit.prototype.depth; + +/** + * @type {boolean} + */ +XRWebGLLayerInit.prototype.stencil; + +/** + * @type {boolean} + */ +XRWebGLLayerInit.prototype.alpha; + +/** + * @type {boolean} + */ +XRWebGLLayerInit.prototype.ignoreDepthValues; + +/** + * @type {boolean} + */ +XRWebGLLayerInit.prototype.ignoreDepthValues; + +/** + * @type {number} + */ +XRWebGLLayerInit.prototype.framebufferScaleFactor; + +/** + * @constructor + * + * @param {XRSession} session + * @param {WebGLRenderContext|WebGL2RenderingContext} ctx + * @param {?XRWebGLLayerInit} options + */ +function XRWebGLLayer(session, ctx, options) {} + +/** + * @type {boolean} + */ +XRWebGLLayer.prototype.antialias; + +/** + * @type {boolean} + */ +XRWebGLLayer.prototype.ignoreDepthValues; + +/** + * @type {number} + */ +XRWebGLLayer.prototype.framebufferWidth; + +/** + * @type {number} + */ +XRWebGLLayer.prototype.framebufferHeight; + +/** + * @type {WebGLFramebuffer} + */ +XRWebGLLayer.prototype.framebuffer; + +/** + * @param {XRView} view + * @return {?XRViewport} + */ +XRWebGLLayer.prototype.getViewport = function(view) {}; + +/** + * @param {XRSession} session + * @return {number} + */ +XRWebGLLayer.prototype.getNativeFramebufferScaleFactor = function (session) {}; + +/** + * @constructor + */ +function WebGLRenderingContextBase() {}; + +/** + * @return {Promise<void>} + */ +WebGLRenderingContextBase.prototype.makeXRCompatible = function () {}; + +/** + * @constructor + */ +function XRInputSourcesChangeEvent() {}; + +/** + * @type {Array<XRInputSource>} + */ +XRInputSourcesChangeEvent.prototype.added; + +/** + * @type {Array<XRInputSource>} + */ +XRInputSourcesChangeEvent.prototype.removed; + +/** + * @constructor + */ +function XRInputSourceEvent() {}; + +/** + * @type {XRFrame} + */ +XRInputSourceEvent.prototype.frame; + +/** + * @type {XRInputSource} + */ +XRInputSourceEvent.prototype.inputSource; + +/** + * @constructor + */ +function XRInputSource() {}; + +/** + * @type {Gamepad} + */ +XRInputSource.prototype.gamepad; + +/** + * @type {XRSpace} + */ +XRInputSource.prototype.gripSpace; + +/** + * @type {string} + */ +XRInputSource.prototype.handedness; + +/** + * @type {string} + */ +XRInputSource.prototype.profiles; + +/** + * @type {string} + */ +XRInputSource.prototype.targetRayMode; + +/** + * @type {XRSpace} + */ +XRInputSource.prototype.targetRaySpace; + +/** + * @constructor + */ +function XRSpace() {}; + +/** + * @constructor + */ +function XRPose() {}; + +/** + * @type {XRRigidTransform} + */ +XRPose.prototype.transform; + +/** + * @type {boolean} + */ +XRPose.prototype.emulatedPosition; diff --git a/modules/webxr/register_types.cpp b/modules/webxr/register_types.cpp new file mode 100644 index 0000000000..8baf7e05b8 --- /dev/null +++ b/modules/webxr/register_types.cpp @@ -0,0 +1,47 @@ +/*************************************************************************/ +/* register_types.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "register_types.h" + +#include "webxr_interface.h" +#include "webxr_interface_js.h" + +void register_webxr_types() { + ClassDB::register_virtual_class<WebXRInterface>(); + +#ifdef JAVASCRIPT_ENABLED + Ref<WebXRInterfaceJS> webxr; + webxr.instance(); + XRServer::get_singleton()->add_interface(webxr); +#endif +} + +void unregister_webxr_types() { +} diff --git a/modules/webxr/register_types.h b/modules/webxr/register_types.h new file mode 100644 index 0000000000..f0c5a4bd79 --- /dev/null +++ b/modules/webxr/register_types.h @@ -0,0 +1,37 @@ +/*************************************************************************/ +/* register_types.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef WEBXR_REGISTER_TYPES_H +#define WEBXR_REGISTER_TYPES_H + +void register_webxr_types(); +void unregister_webxr_types(); + +#endif // WEBXR_REGISTER_TYPES_H diff --git a/modules/webxr/webxr_interface.cpp b/modules/webxr/webxr_interface.cpp new file mode 100644 index 0000000000..3e8e75bf0e --- /dev/null +++ b/modules/webxr/webxr_interface.cpp @@ -0,0 +1,71 @@ +/*************************************************************************/ +/* webxr_interface.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "webxr_interface.h" +#include <stdlib.h> + +void WebXRInterface::_bind_methods() { + ClassDB::bind_method(D_METHOD("is_session_supported", "session_mode"), &WebXRInterface::is_session_supported); + ClassDB::bind_method(D_METHOD("set_session_mode", "session_mode"), &WebXRInterface::set_session_mode); + ClassDB::bind_method(D_METHOD("get_session_mode"), &WebXRInterface::get_session_mode); + ClassDB::bind_method(D_METHOD("set_required_features", "required_features"), &WebXRInterface::set_required_features); + ClassDB::bind_method(D_METHOD("get_required_features"), &WebXRInterface::get_required_features); + ClassDB::bind_method(D_METHOD("set_optional_features", "optional_features"), &WebXRInterface::set_optional_features); + ClassDB::bind_method(D_METHOD("get_optional_features"), &WebXRInterface::get_optional_features); + ClassDB::bind_method(D_METHOD("get_reference_space_type"), &WebXRInterface::get_reference_space_type); + ClassDB::bind_method(D_METHOD("set_requested_reference_space_types", "requested_reference_space_types"), &WebXRInterface::set_requested_reference_space_types); + ClassDB::bind_method(D_METHOD("get_requested_reference_space_types"), &WebXRInterface::get_requested_reference_space_types); + ClassDB::bind_method(D_METHOD("get_controller", "controller_id"), &WebXRInterface::get_controller); + ClassDB::bind_method(D_METHOD("get_visibility_state"), &WebXRInterface::get_visibility_state); + ClassDB::bind_method(D_METHOD("get_bounds_geometry"), &WebXRInterface::get_bounds_geometry); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "session_mode", PROPERTY_HINT_NONE), "set_session_mode", "get_session_mode"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "required_features", PROPERTY_HINT_NONE), "set_required_features", "get_required_features"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "optional_features", PROPERTY_HINT_NONE), "set_optional_features", "get_optional_features"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "requested_reference_space_types", PROPERTY_HINT_NONE), "set_requested_reference_space_types", "get_requested_reference_space_types"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "reference_space_type", PROPERTY_HINT_NONE), "", "get_reference_space_type"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "visibility_state", PROPERTY_HINT_NONE), "", "get_visibility_state"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "bounds_geometry", PROPERTY_HINT_NONE), "", "get_bounds_geometry"); + + ADD_SIGNAL(MethodInfo("session_supported", PropertyInfo(Variant::STRING, "session_mode"), PropertyInfo(Variant::BOOL, "supported"))); + ADD_SIGNAL(MethodInfo("session_started")); + ADD_SIGNAL(MethodInfo("session_ended")); + ADD_SIGNAL(MethodInfo("session_failed", PropertyInfo(Variant::STRING, "message"))); + + ADD_SIGNAL(MethodInfo("selectstart", PropertyInfo(Variant::INT, "controller_id"))); + ADD_SIGNAL(MethodInfo("select", PropertyInfo(Variant::INT, "controller_id"))); + ADD_SIGNAL(MethodInfo("selectend", PropertyInfo(Variant::INT, "controller_id"))); + ADD_SIGNAL(MethodInfo("squeezestart", PropertyInfo(Variant::INT, "controller_id"))); + ADD_SIGNAL(MethodInfo("squeeze", PropertyInfo(Variant::INT, "controller_id"))); + ADD_SIGNAL(MethodInfo("squeezeend", PropertyInfo(Variant::INT, "controller_id"))); + + ADD_SIGNAL(MethodInfo("visibility_state_changed")); + ADD_SIGNAL(MethodInfo("reference_space_reset")); +} diff --git a/modules/webxr/webxr_interface.h b/modules/webxr/webxr_interface.h new file mode 100644 index 0000000000..c5b2dc8d73 --- /dev/null +++ b/modules/webxr/webxr_interface.h @@ -0,0 +1,65 @@ +/*************************************************************************/ +/* webxr_interface.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef WEBXR_INTERFACE_H +#define WEBXR_INTERFACE_H + +#include "servers/xr/xr_interface.h" +#include "servers/xr/xr_positional_tracker.h" + +/** + @author David Snopek <david.snopek@snopekgames.com> + + The WebXR interface is a VR/AR interface that can be used on the web. +*/ + +class WebXRInterface : public XRInterface { + GDCLASS(WebXRInterface, XRInterface); + +protected: + static void _bind_methods(); + +public: + virtual void is_session_supported(const String &p_session_mode) = 0; + virtual void set_session_mode(String p_session_mode) = 0; + virtual String get_session_mode() const = 0; + virtual void set_required_features(String p_required_features) = 0; + virtual String get_required_features() const = 0; + virtual void set_optional_features(String p_optional_features) = 0; + virtual String get_optional_features() const = 0; + virtual void set_requested_reference_space_types(String p_requested_reference_space_types) = 0; + virtual String get_requested_reference_space_types() const = 0; + virtual String get_reference_space_type() const = 0; + virtual XRPositionalTracker *get_controller(int p_controller_id) const = 0; + virtual String get_visibility_state() const = 0; + virtual PackedVector3Array get_bounds_geometry() const = 0; +}; + +#endif // WEBXR_INTERFACE_H diff --git a/modules/webxr/webxr_interface_js.cpp b/modules/webxr/webxr_interface_js.cpp new file mode 100644 index 0000000000..6594553146 --- /dev/null +++ b/modules/webxr/webxr_interface_js.cpp @@ -0,0 +1,451 @@ +/*************************************************************************/ +/* webxr_interface_js.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifdef JAVASCRIPT_ENABLED + +#include "webxr_interface_js.h" +#include "core/input/input.h" +#include "core/os/os.h" +#include "emscripten.h" +#include "godot_webxr.h" +#include <stdlib.h> + +void _emwebxr_on_session_supported(char *p_session_mode, int p_supported) { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); + + Ref<XRInterface> interface = xr_server->find_interface("WebXR"); + ERR_FAIL_COND(interface.is_null()); + + String session_mode = String(p_session_mode); + interface->emit_signal("session_supported", session_mode, p_supported ? true : false); +} + +void _emwebxr_on_session_started(char *p_reference_space_type) { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); + + Ref<XRInterface> interface = xr_server->find_interface("WebXR"); + ERR_FAIL_COND(interface.is_null()); + + String reference_space_type = String(p_reference_space_type); + ((WebXRInterfaceJS *)interface.ptr())->_set_reference_space_type(reference_space_type); + interface->emit_signal("session_started"); +} + +void _emwebxr_on_session_ended() { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); + + Ref<XRInterface> interface = xr_server->find_interface("WebXR"); + ERR_FAIL_COND(interface.is_null()); + + interface->uninitialize(); + interface->emit_signal("session_ended"); +} + +void _emwebxr_on_session_failed(char *p_message) { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); + + Ref<XRInterface> interface = xr_server->find_interface("WebXR"); + ERR_FAIL_COND(interface.is_null()); + + String message = String(p_message); + interface->emit_signal("session_failed", message); +} + +void _emwebxr_on_controller_changed() { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); + + Ref<XRInterface> interface = xr_server->find_interface("WebXR"); + ERR_FAIL_COND(interface.is_null()); + + ((WebXRInterfaceJS *)interface.ptr())->_on_controller_changed(); +} + +extern "C" EMSCRIPTEN_KEEPALIVE void _emwebxr_on_input_event(char *p_signal_name, int p_input_source) { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); + + Ref<XRInterface> interface = xr_server->find_interface("WebXR"); + ERR_FAIL_COND(interface.is_null()); + + StringName signal_name = StringName(p_signal_name); + interface->emit_signal(signal_name, p_input_source + 1); +} + +extern "C" EMSCRIPTEN_KEEPALIVE void _emwebxr_on_simple_event(char *p_signal_name) { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); + + Ref<XRInterface> interface = xr_server->find_interface("WebXR"); + ERR_FAIL_COND(interface.is_null()); + + StringName signal_name = StringName(p_signal_name); + interface->emit_signal(signal_name); +} + +void WebXRInterfaceJS::is_session_supported(const String &p_session_mode) { + godot_webxr_is_session_supported(p_session_mode.utf8().get_data(), &_emwebxr_on_session_supported); +} + +void WebXRInterfaceJS::set_session_mode(String p_session_mode) { + session_mode = p_session_mode; +} + +String WebXRInterfaceJS::get_session_mode() const { + return session_mode; +} + +void WebXRInterfaceJS::set_required_features(String p_required_features) { + required_features = p_required_features; +} + +String WebXRInterfaceJS::get_required_features() const { + return required_features; +} + +void WebXRInterfaceJS::set_optional_features(String p_optional_features) { + optional_features = p_optional_features; +} + +String WebXRInterfaceJS::get_optional_features() const { + return optional_features; +} + +void WebXRInterfaceJS::set_requested_reference_space_types(String p_requested_reference_space_types) { + requested_reference_space_types = p_requested_reference_space_types; +} + +String WebXRInterfaceJS::get_requested_reference_space_types() const { + return requested_reference_space_types; +} + +void WebXRInterfaceJS::_set_reference_space_type(String p_reference_space_type) { + reference_space_type = p_reference_space_type; +} + +String WebXRInterfaceJS::get_reference_space_type() const { + return reference_space_type; +} + +XRPositionalTracker *WebXRInterfaceJS::get_controller(int p_controller_id) const { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL_V(xr_server, nullptr); + + return xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id); +} + +String WebXRInterfaceJS::get_visibility_state() const { + char *c_str = godot_webxr_get_visibility_state(); + if (c_str) { + String visibility_state = String(c_str); + free(c_str); + + return visibility_state; + } + return String(); +} + +PackedVector3Array WebXRInterfaceJS::get_bounds_geometry() const { + PackedVector3Array ret; + + int *js_bounds = godot_webxr_get_bounds_geometry(); + if (js_bounds) { + ret.resize(js_bounds[0]); + for (int i = 0; i < js_bounds[0]; i++) { + float *js_vector3 = ((float *)js_bounds) + (i * 3) + 1; + ret.set(i, Vector3(js_vector3[0], js_vector3[1], js_vector3[2])); + } + free(js_bounds); + } + + return ret; +} + +StringName WebXRInterfaceJS::get_name() const { + return "WebXR"; +}; + +int WebXRInterfaceJS::get_capabilities() const { + return XRInterface::XR_STEREO; +}; + +bool WebXRInterfaceJS::is_stereo() { + // @todo WebXR can be mono! So, how do we know? Count the views in the frame? + return true; +}; + +bool WebXRInterfaceJS::is_initialized() const { + return (initialized); +}; + +bool WebXRInterfaceJS::initialize() { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL_V(xr_server, false); + + if (!initialized) { + if (!godot_webxr_is_supported()) { + return false; + } + + if (requested_reference_space_types.size() == 0) { + return false; + } + + // make this our primary interface + xr_server->set_primary_interface(this); + + initialized = true; + + godot_webxr_initialize( + session_mode.utf8().get_data(), + required_features.utf8().get_data(), + optional_features.utf8().get_data(), + requested_reference_space_types.utf8().get_data(), + &_emwebxr_on_session_started, + &_emwebxr_on_session_ended, + &_emwebxr_on_session_failed, + &_emwebxr_on_controller_changed, + &_emwebxr_on_input_event, + &_emwebxr_on_simple_event); + }; + + return true; +}; + +void WebXRInterfaceJS::uninitialize() { + if (initialized) { + XRServer *xr_server = XRServer::get_singleton(); + if (xr_server != NULL) { + // no longer our primary interface + xr_server->clear_primary_interface_if(this); + } + + godot_webxr_uninitialize(); + + reference_space_type = ""; + initialized = false; + }; +}; + +Transform WebXRInterfaceJS::_js_matrix_to_transform(float *p_js_matrix) { + Transform transform; + + transform.basis.elements[0].x = p_js_matrix[0]; + transform.basis.elements[1].x = p_js_matrix[1]; + transform.basis.elements[2].x = p_js_matrix[2]; + transform.basis.elements[0].y = p_js_matrix[4]; + transform.basis.elements[1].y = p_js_matrix[5]; + transform.basis.elements[2].y = p_js_matrix[6]; + transform.basis.elements[0].z = p_js_matrix[8]; + transform.basis.elements[1].z = p_js_matrix[9]; + transform.basis.elements[2].z = p_js_matrix[10]; + transform.origin.x = p_js_matrix[12]; + transform.origin.y = p_js_matrix[13]; + transform.origin.z = p_js_matrix[14]; + + return transform; +} + +Size2 WebXRInterfaceJS::get_render_targetsize() { + Size2 target_size; + + int *js_size = godot_webxr_get_render_targetsize(); + if (!initialized || js_size == nullptr) { + // As a default, use half the window size. + target_size = DisplayServer::get_singleton()->window_get_size(); + target_size.width /= 2.0; + return target_size; + } + + target_size.width = js_size[0]; + target_size.height = js_size[1]; + + free(js_size); + + return target_size; +}; + +Transform WebXRInterfaceJS::get_transform_for_eye(XRInterface::Eyes p_eye, const Transform &p_cam_transform) { + Transform transform_for_eye; + + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL_V(xr_server, transform_for_eye); + + float *js_matrix = godot_webxr_get_transform_for_eye(p_eye); + if (!initialized || js_matrix == nullptr) { + transform_for_eye = p_cam_transform; + return transform_for_eye; + } + + transform_for_eye = _js_matrix_to_transform(js_matrix); + free(js_matrix); + + return p_cam_transform * xr_server->get_reference_frame() * transform_for_eye; +}; + +CameraMatrix WebXRInterfaceJS::get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) { + CameraMatrix eye; + + float *js_matrix = godot_webxr_get_projection_for_eye(p_eye); + if (!initialized || js_matrix == nullptr) { + return eye; + } + + int k = 0; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + eye.matrix[i][j] = js_matrix[k++]; + } + } + + free(js_matrix); + + // Copied from godot_oculus_mobile's ovr_mobile_session.cpp + eye.matrix[2][2] = -(p_z_far + p_z_near) / (p_z_far - p_z_near); + eye.matrix[3][2] = -(2.0f * p_z_far * p_z_near) / (p_z_far - p_z_near); + + return eye; +} + +unsigned int WebXRInterfaceJS::get_external_texture_for_eye(XRInterface::Eyes p_eye) { + if (!initialized) { + return 0; + } + return godot_webxr_get_external_texture_for_eye(p_eye); +} + +void WebXRInterfaceJS::commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) { + if (!initialized) { + return; + } + godot_webxr_commit_for_eye(p_eye); +}; + +void WebXRInterfaceJS::process() { + if (initialized) { + godot_webxr_sample_controller_data(); + + int controller_count = godot_webxr_get_controller_count(); + if (controller_count == 0) { + return; + } + + for (int i = 0; i < controller_count; i++) { + _update_tracker(i); + } + }; +}; + +void WebXRInterfaceJS::_update_tracker(int p_controller_id) { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); + + XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id + 1); + if (godot_webxr_is_controller_connected(p_controller_id)) { + if (tracker == nullptr) { + tracker = memnew(XRPositionalTracker); + tracker->set_tracker_type(XRServer::TRACKER_CONTROLLER); + // Controller id's 0 and 1 are always the left and right hands. + if (p_controller_id < 2) { + tracker->set_tracker_name(p_controller_id == 0 ? "Left" : "Right"); + tracker->set_tracker_hand(p_controller_id == 0 ? XRPositionalTracker::TRACKER_HAND_LEFT : XRPositionalTracker::TRACKER_HAND_RIGHT); + } + // Use the ids we're giving to our "virtual" gamepads. + tracker->set_joy_id(p_controller_id + 100); + xr_server->add_tracker(tracker); + } + + Input *input = Input::get_singleton(); + + float *tracker_matrix = godot_webxr_get_controller_transform(p_controller_id); + if (tracker_matrix) { + Transform transform = _js_matrix_to_transform(tracker_matrix); + tracker->set_position(transform.origin); + tracker->set_orientation(transform.basis); + free(tracker_matrix); + } + + int *buttons = godot_webxr_get_controller_buttons(p_controller_id); + if (buttons) { + for (int i = 0; i < buttons[0]; i++) { + input->joy_button(p_controller_id + 100, i, *((float *)buttons + (i + 1))); + } + free(buttons); + } + + int *axes = godot_webxr_get_controller_axes(p_controller_id); + if (axes) { + for (int i = 0; i < axes[0]; i++) { + Input::JoyAxis joy_axis; + joy_axis.min = -1; + joy_axis.value = *((float *)axes + (i + 1)); + input->joy_axis(p_controller_id + 100, i, joy_axis); + } + free(axes); + } + } else if (tracker) { + xr_server->remove_tracker(tracker); + } +} + +void WebXRInterfaceJS::_on_controller_changed() { + // Register "virtual" gamepads with Godot for the ones we get from WebXR. + godot_webxr_sample_controller_data(); + for (int i = 0; i < 2; i++) { + bool controller_connected = godot_webxr_is_controller_connected(i); + if (controllers_state[i] != controller_connected) { + Input::get_singleton()->joy_connection_changed(i + 100, controller_connected, i == 0 ? "Left" : "Right", ""); + controllers_state[i] = controller_connected; + } + } +} + +void WebXRInterfaceJS::notification(int p_what) { + // Nothing to do here. +} + +WebXRInterfaceJS::WebXRInterfaceJS() { + initialized = false; + session_mode = "inline"; + requested_reference_space_types = "local"; +}; + +WebXRInterfaceJS::~WebXRInterfaceJS() { + // and make sure we cleanup if we haven't already + if (initialized) { + uninitialize(); + }; +}; + +#endif // JAVASCRIPT_ENABLED diff --git a/modules/webxr/webxr_interface_js.h b/modules/webxr/webxr_interface_js.h new file mode 100644 index 0000000000..93da9a6d12 --- /dev/null +++ b/modules/webxr/webxr_interface_js.h @@ -0,0 +1,103 @@ +/*************************************************************************/ +/* webxr_interface_js.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef WEBXR_INTERFACE_JS_H +#define WEBXR_INTERFACE_JS_H + +#ifdef JAVASCRIPT_ENABLED + +#include "webxr_interface.h" + +/** + @author David Snopek <david.snopek@snopekgames.com> + + The WebXR interface is a VR/AR interface that can be used on the web. +*/ + +class WebXRInterfaceJS : public WebXRInterface { + GDCLASS(WebXRInterfaceJS, WebXRInterface); + +private: + bool initialized; + + // @todo Should these really use enums instead of strings? + String session_mode; + String required_features; + String optional_features; + String requested_reference_space_types; + String reference_space_type; + + bool controllers_state[2]; + + Transform _js_matrix_to_transform(float *p_js_matrix); + void _update_tracker(int p_controller_id); + +public: + virtual void is_session_supported(const String &p_session_mode) override; + virtual void set_session_mode(String p_session_mode) override; + virtual String get_session_mode() const override; + virtual void set_required_features(String p_required_features) override; + virtual String get_required_features() const override; + virtual void set_optional_features(String p_optional_features) override; + virtual String get_optional_features() const override; + virtual void set_requested_reference_space_types(String p_requested_reference_space_types) override; + virtual String get_requested_reference_space_types() const override; + void _set_reference_space_type(String p_reference_space_type); + virtual String get_reference_space_type() const override; + virtual XRPositionalTracker *get_controller(int p_controller_id) const override; + virtual String get_visibility_state() const override; + virtual PackedVector3Array get_bounds_geometry() const override; + + virtual StringName get_name() const override; + virtual int get_capabilities() const override; + + virtual bool is_initialized() const override; + virtual bool initialize() override; + virtual void uninitialize() override; + + virtual Size2 get_render_targetsize() override; + virtual bool is_stereo() override; + virtual Transform get_transform_for_eye(XRInterface::Eyes p_eye, const Transform &p_cam_transform) override; + virtual CameraMatrix get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) override; + virtual unsigned int get_external_texture_for_eye(XRInterface::Eyes p_eye) override; + virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) override; + + virtual void process() override; + virtual void notification(int p_what) override; + + void _on_controller_changed(); + + WebXRInterfaceJS(); + ~WebXRInterfaceJS(); +}; + +#endif // JAVASCRIPT_ENABLED + +#endif // WEBXR_INTERFACE_JS_H diff --git a/modules/xatlas_unwrap/SCsub b/modules/xatlas_unwrap/SCsub index c659349d05..aa6bdaea33 100644 --- a/modules/xatlas_unwrap/SCsub +++ b/modules/xatlas_unwrap/SCsub @@ -6,6 +6,9 @@ Import("env_modules") env_xatlas_unwrap = env_modules.Clone() # Thirdparty source files + +thirdparty_obj = [] + if env["builtin_xatlas"]: thirdparty_dir = "#thirdparty/xatlas/" thirdparty_sources = [ @@ -17,7 +20,16 @@ if env["builtin_xatlas"]: env_thirdparty = env_xatlas_unwrap.Clone() env_thirdparty.disable_warnings() - env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) + env.modules_sources += thirdparty_obj + # Godot source files -env_xatlas_unwrap.add_source_files(env.modules_sources, "*.cpp") + +module_obj = [] + +env_xatlas_unwrap.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj + +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/xatlas_unwrap/register_types.cpp b/modules/xatlas_unwrap/register_types.cpp index 65350518c3..9f6e7efb27 100644 --- a/modules/xatlas_unwrap/register_types.cpp +++ b/modules/xatlas_unwrap/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -141,11 +141,11 @@ bool xatlas_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_ver xatlas::Atlas *atlas = xatlas::Create(); printf("Adding mesh..\n"); - xatlas::AddMeshError::Enum err = xatlas::AddMesh(atlas, input_mesh, 1); - ERR_FAIL_COND_V_MSG(err != xatlas::AddMeshError::Enum::Success, false, xatlas::StringForEnum(err)); + xatlas::AddMeshError err = xatlas::AddMesh(atlas, input_mesh, 1); + ERR_FAIL_COND_V_MSG(err != xatlas::AddMeshError::Success, false, xatlas::StringForEnum(err)); printf("Generate..\n"); - xatlas::Generate(atlas, chart_options, xatlas::ParameterizeOptions(), pack_options); + xatlas::Generate(atlas, chart_options, pack_options); *r_size_hint_x = atlas->width; *r_size_hint_y = atlas->height; diff --git a/modules/xatlas_unwrap/register_types.h b/modules/xatlas_unwrap/register_types.h index fe924bab96..2ad729f172 100644 --- a/modules/xatlas_unwrap/register_types.h +++ b/modules/xatlas_unwrap/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ |