From 7c4416503eeb761042b529786c170c51b5a69301 Mon Sep 17 00:00:00 2001 From: Relintai Date: Sun, 7 Aug 2022 20:52:06 +0200 Subject: [PATCH] Replaced the gltf modlue to the one in godot4. It's currently disabled by default while it can't be built. --- modules/gltf/README.md | 11 + modules/gltf/SCsub | 7 +- modules/gltf/config.py | 12 +- .../EditorSceneFormatImporterBlend.xml | 15 + .../EditorSceneFormatImporterFBX.xml | 13 + .../EditorSceneFormatImporterGLTF.xml | 9 + .../doc_classes/EditorSceneImporterGLTF.xml | 14 - modules/gltf/doc_classes/GLTFAccessor.xml | 11 +- modules/gltf/doc_classes/GLTFAnimation.xml | 7 +- modules/gltf/doc_classes/GLTFBufferView.xml | 7 +- modules/gltf/doc_classes/GLTFCamera.xml | 15 +- modules/gltf/doc_classes/GLTFDocument.xml | 59 +- .../doc_classes/GLTFDocumentExtension.xml | 60 + ...TFDocumentExtensionConvertImporterMesh.xml | 9 + modules/gltf/doc_classes/GLTFLight.xml | 23 +- modules/gltf/doc_classes/GLTFMesh.xml | 13 +- modules/gltf/doc_classes/GLTFNode.xml | 19 +- modules/gltf/doc_classes/GLTFSkeleton.xml | 21 +- modules/gltf/doc_classes/GLTFSkin.xml | 14 +- modules/gltf/doc_classes/GLTFSpecGloss.xml | 11 +- modules/gltf/doc_classes/GLTFState.xml | 13 +- modules/gltf/doc_classes/GLTFTexture.xml | 7 +- modules/gltf/doc_classes/PackedSceneGLTF.xml | 46 - .../editor_scene_exporter_gltf_plugin.cpp | 65 +- .../editor_scene_exporter_gltf_plugin.h | 26 +- .../editor/editor_scene_importer_blend.cpp | 575 +++ .../gltf/editor/editor_scene_importer_blend.h | 112 + .../gltf/editor/editor_scene_importer_fbx.cpp | 117 + .../editor_scene_importer_fbx.h} | 45 +- .../editor_scene_importer_gltf.cpp | 40 +- .../{ => editor}/editor_scene_importer_gltf.h | 31 +- modules/gltf/{ => extensions}/gltf_light.cpp | 22 +- modules/gltf/{ => extensions}/gltf_light.h | 15 +- .../gltf/{ => extensions}/gltf_spec_gloss.cpp | 2 +- .../gltf/{ => extensions}/gltf_spec_gloss.h | 9 +- modules/gltf/gltf_defines.h | 87 + modules/gltf/gltf_document.cpp | 3093 +++++++++-------- modules/gltf/gltf_document.h | 220 +- modules/gltf/gltf_document_extension.cpp | 108 + modules/gltf/gltf_document_extension.h | 60 + ...cument_extension_convert_importer_mesh.cpp | 81 + ...document_extension_convert_importer_mesh.h | 50 + modules/gltf/gltf_state.cpp | 95 +- modules/gltf/gltf_state.h | 78 +- modules/gltf/gltf_template_convert.h | 94 + modules/gltf/packed_scene_gltf.cpp | 160 - modules/gltf/register_types.cpp | 171 +- modules/gltf/register_types.h | 8 - .../gltf/{ => structures}/gltf_accessor.cpp | 16 +- modules/gltf/{ => structures}/gltf_accessor.h | 23 +- .../gltf/{ => structures}/gltf_animation.cpp | 2 +- .../gltf/{ => structures}/gltf_animation.h | 19 +- .../{ => structures}/gltf_buffer_view.cpp | 2 + .../gltf/{ => structures}/gltf_buffer_view.h | 8 +- modules/gltf/{ => structures}/gltf_camera.cpp | 14 +- modules/gltf/{ => structures}/gltf_camera.h | 19 +- modules/gltf/{ => structures}/gltf_mesh.cpp | 8 +- modules/gltf/{ => structures}/gltf_mesh.h | 15 +- modules/gltf/{ => structures}/gltf_node.cpp | 28 +- modules/gltf/{ => structures}/gltf_node.h | 27 +- .../gltf/{ => structures}/gltf_skeleton.cpp | 41 +- modules/gltf/{ => structures}/gltf_skeleton.h | 58 +- modules/gltf/{ => structures}/gltf_skin.cpp | 51 +- modules/gltf/{ => structures}/gltf_skin.h | 27 +- .../gltf/{ => structures}/gltf_texture.cpp | 0 modules/gltf/{ => structures}/gltf_texture.h | 8 +- 66 files changed, 3874 insertions(+), 2272 deletions(-) create mode 100644 modules/gltf/README.md create mode 100644 modules/gltf/doc_classes/EditorSceneFormatImporterBlend.xml create mode 100644 modules/gltf/doc_classes/EditorSceneFormatImporterFBX.xml create mode 100644 modules/gltf/doc_classes/EditorSceneFormatImporterGLTF.xml delete mode 100644 modules/gltf/doc_classes/EditorSceneImporterGLTF.xml create mode 100644 modules/gltf/doc_classes/GLTFDocumentExtension.xml create mode 100644 modules/gltf/doc_classes/GLTFDocumentExtensionConvertImporterMesh.xml delete mode 100644 modules/gltf/doc_classes/PackedSceneGLTF.xml rename modules/gltf/{ => editor}/editor_scene_exporter_gltf_plugin.cpp (61%) rename modules/gltf/{ => editor}/editor_scene_exporter_gltf_plugin.h (88%) create mode 100644 modules/gltf/editor/editor_scene_importer_blend.cpp create mode 100644 modules/gltf/editor/editor_scene_importer_blend.h create mode 100644 modules/gltf/editor/editor_scene_importer_fbx.cpp rename modules/gltf/{packed_scene_gltf.h => editor/editor_scene_importer_fbx.h} (65%) rename modules/gltf/{ => editor}/editor_scene_importer_gltf.cpp (77%) rename modules/gltf/{ => editor}/editor_scene_importer_gltf.h (83%) rename modules/gltf/{ => extensions}/gltf_light.cpp (82%) rename modules/gltf/{ => extensions}/gltf_light.h (94%) rename modules/gltf/{ => extensions}/gltf_spec_gloss.cpp (97%) rename modules/gltf/{ => extensions}/gltf_spec_gloss.h (98%) create mode 100644 modules/gltf/gltf_defines.h create mode 100644 modules/gltf/gltf_document_extension.cpp create mode 100644 modules/gltf/gltf_document_extension.h create mode 100644 modules/gltf/gltf_document_extension_convert_importer_mesh.cpp create mode 100644 modules/gltf/gltf_document_extension_convert_importer_mesh.h create mode 100644 modules/gltf/gltf_template_convert.h delete mode 100644 modules/gltf/packed_scene_gltf.cpp rename modules/gltf/{ => structures}/gltf_accessor.cpp (94%) rename modules/gltf/{ => structures}/gltf_accessor.h (92%) rename modules/gltf/{ => structures}/gltf_animation.cpp (97%) rename modules/gltf/{ => structures}/gltf_animation.h (91%) rename modules/gltf/{ => structures}/gltf_buffer_view.cpp (99%) rename modules/gltf/{ => structures}/gltf_buffer_view.h (98%) rename modules/gltf/{ => structures}/gltf_camera.cpp (80%) rename modules/gltf/{ => structures}/gltf_camera.h (89%) rename modules/gltf/{ => structures}/gltf_mesh.cpp (92%) rename modules/gltf/{ => structures}/gltf_mesh.h (92%) rename modules/gltf/{ => structures}/gltf_node.cpp (87%) rename modules/gltf/{ => structures}/gltf_node.h (91%) rename modules/gltf/{ => structures}/gltf_skeleton.cpp (69%) rename modules/gltf/{ => structures}/gltf_skeleton.h (67%) rename modules/gltf/{ => structures}/gltf_skin.cpp (80%) rename modules/gltf/{ => structures}/gltf_skin.h (92%) rename modules/gltf/{ => structures}/gltf_texture.cpp (100%) rename modules/gltf/{ => structures}/gltf_texture.h (97%) diff --git a/modules/gltf/README.md b/modules/gltf/README.md new file mode 100644 index 000000000..5d8966b20 --- /dev/null +++ b/modules/gltf/README.md @@ -0,0 +1,11 @@ +# Godot GLTF import and export module + +In a nutshell, the GLTF module works like this: + +* The [`structures/`](structures/) folder contains GLTF structures, the + small pieces that make up a GLTF file, represented as C++ classes. +* The [`extensions/`](extensions/) folder contains GLTF extensions, which + are optional features that build on top of the base GLTF spec. +* [`GLTFState`](gltf_state.h) holds collections of structures and extensions. +* [`GLTFDocument`](gltf_document.h) operates on GLTFState and its elements. +* The [`editor/`](editor/) folder uses GLTFDocument to import and export 3D models. diff --git a/modules/gltf/SCsub b/modules/gltf/SCsub index f23d5675c..a7cdeca2e 100644 --- a/modules/gltf/SCsub +++ b/modules/gltf/SCsub @@ -5,5 +5,10 @@ Import("env_modules") env_gltf = env_modules.Clone() -# Pandemonium's own source files +# Godot source files env_gltf.add_source_files(env.modules_sources, "*.cpp") +env_gltf.add_source_files(env.modules_sources, "extensions/*.cpp") +env_gltf.add_source_files(env.modules_sources, "structures/*.cpp") + +if env["tools"]: + env_gltf.add_source_files(env.modules_sources, "editor/*.cpp") diff --git a/modules/gltf/config.py b/modules/gltf/config.py index a4ee871ef..33e9652ae 100644 --- a/modules/gltf/config.py +++ b/modules/gltf/config.py @@ -1,5 +1,8 @@ + + def can_build(env, platform): - return env["tools"] and not env["disable_3d"] + #return not env["disable_3d"] + return False def configure(env): @@ -8,12 +11,16 @@ def configure(env): def get_doc_classes(): return [ - "EditorSceneImporterGLTF", + "EditorSceneFormatImporterBlend", + "EditorSceneFormatImporterFBX", + "EditorSceneFormatImporterGLTF", "GLTFAccessor", "GLTFAnimation", "GLTFBufferView", "GLTFCamera", "GLTFDocument", + "GLTFDocumentExtension", + "GLTFDocumentExtensionConvertImporterMesh", "GLTFLight", "GLTFMesh", "GLTFNode", @@ -22,7 +29,6 @@ def get_doc_classes(): "GLTFSpecGloss", "GLTFState", "GLTFTexture", - "PackedSceneGLTF", ] diff --git a/modules/gltf/doc_classes/EditorSceneFormatImporterBlend.xml b/modules/gltf/doc_classes/EditorSceneFormatImporterBlend.xml new file mode 100644 index 000000000..ca8eb9854 --- /dev/null +++ b/modules/gltf/doc_classes/EditorSceneFormatImporterBlend.xml @@ -0,0 +1,15 @@ + + + + Importer for Blender's [code].blend[/code] scene file format. + + + Imports Blender scenes in the [code].blend[/code] file format through the glTF 2.0 3D import pipeline. This importer requires Blender to be installed by the user, so that it can be used to export the scene as glTF 2.0. + The location of the Blender binary is set via the [code]filesystem/import/blender/blender3_path[/code] editor setting. + This importer is only used if [member ProjectSettings.filesystem/import/blender/enabled] is enabled, otherwise [code].blend[/code] files present in the project folder are not imported. + Blend import requires Blender 3.0. + Internally, the EditorSceneFormatImporterBlend uses the Blender glTF "Use Original" mode to reference external textures. + + + + diff --git a/modules/gltf/doc_classes/EditorSceneFormatImporterFBX.xml b/modules/gltf/doc_classes/EditorSceneFormatImporterFBX.xml new file mode 100644 index 000000000..6754d963f --- /dev/null +++ b/modules/gltf/doc_classes/EditorSceneFormatImporterFBX.xml @@ -0,0 +1,13 @@ + + + + Importer for the [code].fbx[/code] scene file format. + + + Imports Autodesk FBX 3D scenes by way of converting them to glTF 2.0 using the FBX2glTF command line tool. + The location of the FBX2glTF binary is set via the [code]filesystem/import/fbx/fbx2gltf_path[/code] editor setting. + This importer is only used if [member ProjectSettings.filesystem/import/fbx/enabled] is enabled, otherwise [code].fbx[/code] files present in the project folder are not imported. + + + + diff --git a/modules/gltf/doc_classes/EditorSceneFormatImporterGLTF.xml b/modules/gltf/doc_classes/EditorSceneFormatImporterGLTF.xml new file mode 100644 index 000000000..5a6a2f52d --- /dev/null +++ b/modules/gltf/doc_classes/EditorSceneFormatImporterGLTF.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/modules/gltf/doc_classes/EditorSceneImporterGLTF.xml b/modules/gltf/doc_classes/EditorSceneImporterGLTF.xml deleted file mode 100644 index 60853edc1..000000000 --- a/modules/gltf/doc_classes/EditorSceneImporterGLTF.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - [b]Note:[/b] This class is only compiled in editor builds. Run-time glTF loading and saving is [i]not[/i] available in exported projects. References to [EditorSceneImporterGLTF] within a script will cause an error in an exported project. - - - - - - - - diff --git a/modules/gltf/doc_classes/GLTFAccessor.xml b/modules/gltf/doc_classes/GLTFAccessor.xml index 2052ec2ec..b73a4f8c5 100644 --- a/modules/gltf/doc_classes/GLTFAccessor.xml +++ b/modules/gltf/doc_classes/GLTFAccessor.xml @@ -1,14 +1,11 @@ - + - [b]Note:[/b] This class is only compiled in editor builds. Run-time glTF loading and saving is [i]not[/i] available in exported projects. References to [GLTFAccessor] within a script will cause an error in an exported project. - - @@ -18,9 +15,9 @@ - + - + @@ -39,6 +36,4 @@ - - diff --git a/modules/gltf/doc_classes/GLTFAnimation.xml b/modules/gltf/doc_classes/GLTFAnimation.xml index 032794dd8..e2991170a 100644 --- a/modules/gltf/doc_classes/GLTFAnimation.xml +++ b/modules/gltf/doc_classes/GLTFAnimation.xml @@ -1,18 +1,13 @@ - + - [b]Note:[/b] This class is only compiled in editor builds. Run-time glTF loading and saving is [i]not[/i] available in exported projects. References to [GLTFAnimation] within a script will cause an error in an exported project. - - - - diff --git a/modules/gltf/doc_classes/GLTFBufferView.xml b/modules/gltf/doc_classes/GLTFBufferView.xml index 7ebdc463d..00b6ccbe7 100644 --- a/modules/gltf/doc_classes/GLTFBufferView.xml +++ b/modules/gltf/doc_classes/GLTFBufferView.xml @@ -1,14 +1,11 @@ - + - [b]Note:[/b] This class is only compiled in editor builds. Run-time glTF loading and saving is [i]not[/i] available in exported projects. References to [GLTFBufferView] within a script will cause an error in an exported project. - - @@ -21,6 +18,4 @@ - - diff --git a/modules/gltf/doc_classes/GLTFCamera.xml b/modules/gltf/doc_classes/GLTFCamera.xml index 3c8b0a776..9b9eff614 100644 --- a/modules/gltf/doc_classes/GLTFCamera.xml +++ b/modules/gltf/doc_classes/GLTFCamera.xml @@ -1,24 +1,19 @@ - + - [b]Note:[/b] This class is only compiled in editor builds. Run-time glTF loading and saving is [i]not[/i] available in exported projects. References to [GLTFCamera] within a script will cause an error in an exported project. - - + + + + - - - - - - diff --git a/modules/gltf/doc_classes/GLTFDocument.xml b/modules/gltf/doc_classes/GLTFDocument.xml index bde28758a..cb0e3b675 100644 --- a/modules/gltf/doc_classes/GLTFDocument.xml +++ b/modules/gltf/doc_classes/GLTFDocument.xml @@ -1,14 +1,65 @@ - + - [b]Note:[/b] This class is only compiled in editor builds. Run-time glTF loading and saving is [i]not[/i] available in exported projects. References to [GLTFDocument] within a script will cause an error in an exported project. + Append a glTF2 3d format from a file, buffer or scene and then write to the filesystem, buffer or scene. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + diff --git a/modules/gltf/doc_classes/GLTFDocumentExtension.xml b/modules/gltf/doc_classes/GLTFDocumentExtension.xml new file mode 100644 index 000000000..3c28546ad --- /dev/null +++ b/modules/gltf/doc_classes/GLTFDocumentExtension.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/gltf/doc_classes/GLTFDocumentExtensionConvertImporterMesh.xml b/modules/gltf/doc_classes/GLTFDocumentExtensionConvertImporterMesh.xml new file mode 100644 index 000000000..70268fc0c --- /dev/null +++ b/modules/gltf/doc_classes/GLTFDocumentExtensionConvertImporterMesh.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/modules/gltf/doc_classes/GLTFLight.xml b/modules/gltf/doc_classes/GLTFLight.xml index 7f9f7307d..354cd48a0 100644 --- a/modules/gltf/doc_classes/GLTFLight.xml +++ b/modules/gltf/doc_classes/GLTFLight.xml @@ -1,36 +1,31 @@ - + - [b]Note:[/b] This class is only compiled in editor builds. Run-time glTF loading and saving is [i]not[/i] available in exported projects. References to [GLTFLight] within a script will cause an error in an exported project. - - - + The [Color] of the light. Defaults to white. A black color causes the light to have no effect. The inner angle of the cone in a spotlight. Must be less than or equal to the outer cone angle. - Within this angle, the light is at full brightness. Between the inner and outer cone angles, there is a transition from full brightness to zero brightness. When creating a Pandemonium [SpotLight], the ratio between the inner and outer cone angles is used to calculate the attenuation of the light. + Within this angle, the light is at full brightness. Between the inner and outer cone angles, there is a transition from full brightness to zero brightness. When creating a Godot [SpotLight3D], the ratio between the inner and outer cone angles is used to calculate the attenuation of the light. - The intensity of the light. This is expressed in candelas (lumens per steradian) for point and spot lights, and lux (lumens per m²) for directional lights. When creating a Pandemonium light, this value is converted to a unitless multiplier. + The intensity of the light. This is expressed in candelas (lumens per steradian) for point and spot lights, and lux (lumens per m²) for directional lights. When creating a Godot light, this value is converted to a unitless multiplier. + + + The type of the light. The values accepted by Godot are "point", "spot", and "directional", which correspond to Godot's [OmniLight3D], [SpotLight3D], and [DirectionalLight3D] respectively. The outer angle of the cone in a spotlight. Must be greater than or equal to the inner angle. - At this angle, the light drops off to zero brightness. Between the inner and outer cone angles, there is a transition from full brightness to zero brightness. If this angle is a half turn, then the spotlight emits in all directions. When creating a Pandemonium [SpotLight], the outer cone angle is used as the angle of the spotlight. + At this angle, the light drops off to zero brightness. Between the inner and outer cone angles, there is a transition from full brightness to zero brightness. If this angle is a half turn, then the spotlight emits in all directions. When creating a Godot [SpotLight3D], the outer cone angle is used as the angle of the spotlight. - The range of the light, beyond which the light has no effect. GLTF lights with no range defined behave like physical lights (which have infinite range). When creating a Pandemonium light, the range is clamped to 4096. - - - The type of the light. The values accepted by Pandemonium are "point", "spot", and "directional", which correspond to Pandemonium's [OmniLight], [SpotLight], and [DirectionalLight] respectively. + The range of the light, beyond which the light has no effect. GLTF lights with no range defined behave like physical lights (which have infinite range). When creating a Godot light, the range is clamped to 4096. - - diff --git a/modules/gltf/doc_classes/GLTFMesh.xml b/modules/gltf/doc_classes/GLTFMesh.xml index a39cf88af..bac351cc2 100644 --- a/modules/gltf/doc_classes/GLTFMesh.xml +++ b/modules/gltf/doc_classes/GLTFMesh.xml @@ -1,22 +1,17 @@ - + - [b]Note:[/b] This class is only compiled in editor builds. Run-time glTF loading and saving is [i]not[/i] available in exported projects. References to [GLTFMesh] within a script will cause an error in an exported project. - - - + - + - + - - diff --git a/modules/gltf/doc_classes/GLTFNode.xml b/modules/gltf/doc_classes/GLTFNode.xml index dd73b9dc0..e933e6046 100644 --- a/modules/gltf/doc_classes/GLTFNode.xml +++ b/modules/gltf/doc_classes/GLTFNode.xml @@ -1,18 +1,15 @@ - + - [b]Note:[/b] This class is only compiled in editor builds. Run-time glTF loading and saving is [i]not[/i] available in exported projects. References to [GLTFNode] within a script will cause an error in an exported project. - - - + @@ -24,19 +21,17 @@ - + - + + + - - - + - - diff --git a/modules/gltf/doc_classes/GLTFSkeleton.xml b/modules/gltf/doc_classes/GLTFSkeleton.xml index 83b3fd48b..dad985e88 100644 --- a/modules/gltf/doc_classes/GLTFSkeleton.xml +++ b/modules/gltf/doc_classes/GLTFSkeleton.xml @@ -1,15 +1,14 @@ - + - [b]Note:[/b] This class is only compiled in editor builds. Run-time glTF loading and saving is [i]not[/i] available in exported projects. References to [GLTFSkeleton] within a script will cause an error in an exported project. - + @@ -19,13 +18,13 @@ - + - - + + @@ -34,9 +33,9 @@ - + - + @@ -48,11 +47,9 @@ - + - + - - diff --git a/modules/gltf/doc_classes/GLTFSkin.xml b/modules/gltf/doc_classes/GLTFSkin.xml index 2c5bc9a1f..b6a2bdb95 100644 --- a/modules/gltf/doc_classes/GLTFSkin.xml +++ b/modules/gltf/doc_classes/GLTFSkin.xml @@ -1,5 +1,5 @@ - + @@ -42,21 +42,19 @@ - + - + - + - + - + - - diff --git a/modules/gltf/doc_classes/GLTFSpecGloss.xml b/modules/gltf/doc_classes/GLTFSpecGloss.xml index 96c69193b..8433cf8dd 100644 --- a/modules/gltf/doc_classes/GLTFSpecGloss.xml +++ b/modules/gltf/doc_classes/GLTFSpecGloss.xml @@ -1,16 +1,13 @@ - + - [b]Note:[/b] This class is only compiled in editor builds. Run-time glTF loading and saving is [i]not[/i] available in exported projects. References to [GLTFSpecGloss] within a script will cause an error in an exported project. - - - + @@ -18,9 +15,7 @@ - + - - diff --git a/modules/gltf/doc_classes/GLTFState.xml b/modules/gltf/doc_classes/GLTFState.xml index 6e917672a..44a172356 100644 --- a/modules/gltf/doc_classes/GLTFState.xml +++ b/modules/gltf/doc_classes/GLTFState.xml @@ -1,9 +1,8 @@ - + - [b]Note:[/b] This class is only compiled in editor builds. Run-time glTF loading and saving is [i]not[/i] available in exported projects. References to [GLTFState] within a script will cause an error in an exported project. @@ -193,9 +192,11 @@ - + - + + + @@ -203,13 +204,11 @@ - + - - diff --git a/modules/gltf/doc_classes/GLTFTexture.xml b/modules/gltf/doc_classes/GLTFTexture.xml index c81406119..c0bc42416 100644 --- a/modules/gltf/doc_classes/GLTFTexture.xml +++ b/modules/gltf/doc_classes/GLTFTexture.xml @@ -1,18 +1,13 @@ - + - [b]Note:[/b] This class is only compiled in editor builds. Run-time glTF loading and saving is [i]not[/i] available in exported projects. References to [GLTFTexture] within a script will cause an error in an exported project. - - - - diff --git a/modules/gltf/doc_classes/PackedSceneGLTF.xml b/modules/gltf/doc_classes/PackedSceneGLTF.xml deleted file mode 100644 index d618160c0..000000000 --- a/modules/gltf/doc_classes/PackedSceneGLTF.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - [b]Note:[/b] This class is only compiled in editor builds. Run-time glTF loading and saving is [i]not[/i] available in exported projects. References to [PackedSceneGLTF] within a script will cause an error in an exported project. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/modules/gltf/editor_scene_exporter_gltf_plugin.cpp b/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp similarity index 61% rename from modules/gltf/editor_scene_exporter_gltf_plugin.cpp rename to modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp index 841687186..95db1c096 100644 --- a/modules/gltf/editor_scene_exporter_gltf_plugin.cpp +++ b/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp @@ -32,9 +32,18 @@ #include "editor_scene_exporter_gltf_plugin.h" +#include "../gltf_document.h" +#include "../gltf_state.h" + +#include "core/config/project_settings.h" +#include "core/error/error_list.h" +#include "core/object/object.h" +#include "core/templates/vector.h" #include "editor/editor_file_dialog.h" #include "editor/editor_file_system.h" #include "editor/editor_node.h" +#include "scene/3d/mesh_instance_3d.h" +#include "scene/gui/check_box.h" #include "scene/main/node.h" String SceneExporterGLTFPlugin::get_name() const { @@ -45,50 +54,58 @@ bool SceneExporterGLTFPlugin::has_main_screen() const { return false; } -SceneExporterGLTFPlugin::SceneExporterGLTFPlugin(EditorNode *p_node) { - editor = p_node; - convert_gltf2.instance(); +SceneExporterGLTFPlugin::SceneExporterGLTFPlugin() { file_export_lib = memnew(EditorFileDialog); - editor->get_gui_base()->add_child(file_export_lib); - file_export_lib->connect("file_selected", this, "_gltf2_dialog_action"); + EditorNode::get_singleton()->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_mode(EditorFileDialog::MODE_SAVE_FILE); + 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, this, "convert_scene_to_gltf2", DEFVAL(Variant())); + file_export_lib->set_title(TTR("Export Scene to glTF 2.0 File")); + + PopupMenu *menu = get_export_as_menu(); + int idx = menu->get_item_count(); + menu->add_item(TTR("glTF 2.0 Scene...")); + menu->set_item_metadata(idx, 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(); + Node *root = EditorNode::get_singleton()->get_tree()->get_edited_scene_root(); if (!root) { - editor->show_accept(TTR("This operation can't be done without a scene."), TTR("OK")); + EditorNode::get_singleton()->show_accept(TTR("This operation can't be done without a scene."), TTR("OK")); return; } List deps; - convert_gltf2->save_scene(root, p_file, p_file, 0, 1000.0f, &deps); - EditorFileSystem::get_singleton()->scan_changes(); + Ref doc; + doc.instantiate(); + Ref state; + state.instantiate(); + int32_t flags = 0; + flags |= EditorSceneFormatImporter::IMPORT_USE_NAMED_SKIN_BINDS; + Error err = doc->append_from_scene(root, state, flags, 30.0f); + if (err != OK) { + ERR_PRINT(vformat("glTF2 save scene error %s.", itos(err))); + } + err = doc->write_to_filesystem(state, p_file); + if (err != OK) { + ERR_PRINT(vformat("glTF2 save scene error %s.", itos(err))); + } } -void SceneExporterGLTFPlugin::_bind_methods() { - ClassDB::bind_method(D_METHOD("convert_scene_to_gltf2"), &SceneExporterGLTFPlugin::convert_scene_to_gltf2); - ClassDB::bind_method(D_METHOD("_gltf2_dialog_action", "file"), &SceneExporterGLTFPlugin::_gltf2_dialog_action); -} - -void SceneExporterGLTFPlugin::convert_scene_to_gltf2(Variant p_null) { - Node *root = editor->get_tree()->get_edited_scene_root(); +void SceneExporterGLTFPlugin::convert_scene_to_gltf2() { + Node *root = EditorNode::get_singleton()->get_tree()->get_edited_scene_root(); if (!root) { - editor->show_accept(TTR("This operation can't be done without a scene."), TTR("OK")); + EditorNode::get_singleton()->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.empty()) { + String filename = String(root->get_scene_file_path().get_file().get_basename()); + if (filename.is_empty()) { filename = root->get_name(); } - file_export_lib->set_current_file(filename + ".gltf"); + 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/editor_scene_exporter_gltf_plugin.h similarity index 88% rename from modules/gltf/editor_scene_exporter_gltf_plugin.h rename to modules/gltf/editor/editor_scene_exporter_gltf_plugin.h index 97dc17982..5af46bc75 100644 --- a/modules/gltf/editor_scene_exporter_gltf_plugin.h +++ b/modules/gltf/editor/editor_scene_exporter_gltf_plugin.h @@ -1,5 +1,3 @@ -#ifndef EDITOR_SCENE_EXPORTER_GLTF_PLUGIN_H -#define EDITOR_SCENE_EXPORTER_GLTF_PLUGIN_H /*************************************************************************/ /* editor_scene_exporter_gltf_plugin.h */ /*************************************************************************/ @@ -30,27 +28,27 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "editor/editor_file_dialog.h" -#include "editor/editor_plugin.h" +#ifndef EDITOR_SCENE_EXPORTER_GLTF_PLUGIN_H +#define EDITOR_SCENE_EXPORTER_GLTF_PLUGIN_H -#include "packed_scene_gltf.h" +#ifdef TOOLS_ENABLED + +#include "editor/editor_plugin.h" +#include "editor_scene_importer_gltf.h" class SceneExporterGLTFPlugin : public EditorPlugin { GDCLASS(SceneExporterGLTFPlugin, EditorPlugin); - Ref convert_gltf2; - EditorNode *editor = nullptr; EditorFileDialog *file_export_lib = nullptr; void _gltf2_dialog_action(String p_file); - void convert_scene_to_gltf2(Variant p_null); - -protected: - static void _bind_methods(); + void convert_scene_to_gltf2(); public: - virtual String get_name() const; - bool has_main_screen() const; - SceneExporterGLTFPlugin(class EditorNode *p_node); + virtual String get_name() const override; + bool has_main_screen() const override; + SceneExporterGLTFPlugin(); }; +#endif // TOOLS_ENABLED + #endif // EDITOR_SCENE_EXPORTER_GLTF_PLUGIN_H diff --git a/modules/gltf/editor/editor_scene_importer_blend.cpp b/modules/gltf/editor/editor_scene_importer_blend.cpp new file mode 100644 index 000000000..8002c185c --- /dev/null +++ b/modules/gltf/editor/editor_scene_importer_blend.cpp @@ -0,0 +1,575 @@ +/*************************************************************************/ +/* editor_scene_importer_blend.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (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_blend.h" + +#ifdef TOOLS_ENABLED + +#include "../gltf_document.h" +#include "../gltf_state.h" + +#include "core/config/project_settings.h" +#include "editor/editor_file_dialog.h" +#include "editor/editor_node.h" +#include "editor/editor_scale.h" +#include "editor/editor_settings.h" +#include "main/main.h" +#include "scene/main/node.h" +#include "scene/resources/animation.h" + +#ifdef WINDOWS_ENABLED +// Code by Pedro Estebanez (https://github.com/godotengine/godot/pull/59766) +#include +#endif + +uint32_t EditorSceneFormatImporterBlend::get_import_flags() const { + return ImportFlags::IMPORT_SCENE | ImportFlags::IMPORT_ANIMATION; +} + +void EditorSceneFormatImporterBlend::get_extensions(List *r_extensions) const { + r_extensions->push_back("blend"); +} + +Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_t p_flags, + const HashMap &p_options, int p_bake_fps, + List *r_missing_deps, Error *r_err) { + // Get global paths for source and sink. + + // Escape paths to be valid Python strings to embed in the script. + const String source_global = ProjectSettings::get_singleton()->globalize_path(p_path).c_escape(); + const String sink = ProjectSettings::get_singleton()->get_imported_files_path().plus_file( + vformat("%s-%s.gltf", p_path.get_file().get_basename(), p_path.md5_text())); + const String sink_global = ProjectSettings::get_singleton()->globalize_path(sink).c_escape(); + + // Handle configuration options. + + String parameters_arg; + + if (p_options.has(SNAME("blender/nodes/custom_properties")) && p_options[SNAME("blender/nodes/custom_properties")]) { + parameters_arg += "export_extras=True,"; + } else { + parameters_arg += "export_extras=False,"; + } + if (p_options.has(SNAME("blender/meshes/skins")) && p_options[SNAME("blender/meshes/skins")]) { + int32_t skins = p_options["blender/meshes/skins"]; + if (skins == BLEND_BONE_INFLUENCES_NONE) { + parameters_arg += "export_all_influences=False,"; + } else if (skins == BLEND_BONE_INFLUENCES_COMPATIBLE) { + parameters_arg += "export_all_influences=False,"; + } else if (skins == BLEND_BONE_INFLUENCES_ALL) { + parameters_arg += "export_all_influences=True,"; + } + parameters_arg += "export_skins=True,"; + } else { + parameters_arg += "export_skins=False,"; + } + if (p_options.has(SNAME("blender/materials/export_materials")) && p_options[SNAME("blender/materials/export_materials")]) { + int32_t exports = p_options["blender/materials/export_materials"]; + if (exports == BLEND_MATERIAL_EXPORT_PLACEHOLDER) { + parameters_arg += "export_materials='PLACEHOLDER',"; + } else if (exports == BLEND_MATERIAL_EXPORT_EXPORT) { + parameters_arg += "export_materials='EXPORT',"; + } + } else { + parameters_arg += "export_materials='PLACEHOLDER',"; + } + if (p_options.has(SNAME("blender/nodes/cameras")) && p_options[SNAME("blender/nodes/cameras")]) { + parameters_arg += "export_cameras=True,"; + } else { + parameters_arg += "export_cameras=False,"; + } + if (p_options.has(SNAME("blender/nodes/punctual_lights")) && p_options[SNAME("blender/nodes/punctual_lights")]) { + parameters_arg += "export_lights=True,"; + } else { + parameters_arg += "export_lights=False,"; + } + if (p_options.has(SNAME("blender/meshes/colors")) && p_options[SNAME("blender/meshes/colors")]) { + parameters_arg += "export_colors=True,"; + } else { + parameters_arg += "export_colors=False,"; + } + if (p_options.has(SNAME("blender/nodes/visible")) && p_options[SNAME("blender/nodes/visible")]) { + int32_t visible = p_options["blender/nodes/visible"]; + if (visible == BLEND_VISIBLE_VISIBLE_ONLY) { + parameters_arg += "use_visible=True,"; + } else if (visible == BLEND_VISIBLE_RENDERABLE) { + parameters_arg += "use_renderable=True,"; + } else if (visible == BLEND_VISIBLE_ALL) { + parameters_arg += "use_visible=False,use_renderable=False,"; + } + } else { + parameters_arg += "use_visible=False,use_renderable=False,"; + } + + if (p_options.has(SNAME("blender/meshes/uvs")) && p_options[SNAME("blender/meshes/uvs")]) { + parameters_arg += "export_texcoords=True,"; + } else { + parameters_arg += "export_texcoords=False,"; + } + if (p_options.has(SNAME("blender/meshes/normals")) && p_options[SNAME("blender/meshes/normals")]) { + parameters_arg += "export_normals=True,"; + } else { + parameters_arg += "export_normals=False,"; + } + if (p_options.has(SNAME("blender/meshes/tangents")) && p_options[SNAME("blender/meshes/tangents")]) { + parameters_arg += "export_tangents=True,"; + } else { + parameters_arg += "export_tangents=False,"; + } + if (p_options.has(SNAME("blender/animation/group_tracks")) && p_options[SNAME("blender/animation/group_tracks")]) { + parameters_arg += "export_nla_strips=True,"; + } else { + parameters_arg += "export_nla_strips=False,"; + } + if (p_options.has(SNAME("blender/animation/limit_playback")) && p_options[SNAME("blender/animation/limit_playback")]) { + parameters_arg += "export_frame_range=True,"; + } else { + parameters_arg += "export_frame_range=False,"; + } + if (p_options.has(SNAME("blender/animation/always_sample")) && p_options[SNAME("blender/animation/always_sample")]) { + parameters_arg += "export_force_sampling=True,"; + } else { + parameters_arg += "export_force_sampling=False,"; + } + if (p_options.has(SNAME("blender/meshes/export_bones_deforming_mesh_only")) && p_options[SNAME("blender/meshes/export_bones_deforming_mesh_only")]) { + parameters_arg += "export_def_bones=True,"; + } else { + parameters_arg += "export_def_bones=False,"; + } + if (p_options.has(SNAME("blender/nodes/modifiers")) && p_options[SNAME("blender/nodes/modifiers")]) { + parameters_arg += "export_apply=True"; + } else { + parameters_arg += "export_apply=False"; + } + + String unpack_all; + if (p_options.has(SNAME("blender/materials/unpack_enabled")) && p_options[SNAME("blender/materials/unpack_enabled")]) { + unpack_all = "bpy.ops.file.unpack_all(method='USE_LOCAL');"; + } + + // Prepare Blender export script. + + String common_args = vformat("filepath='%s',", sink_global) + + "export_format='GLTF_SEPARATE'," + "export_yup=True," + + parameters_arg; + String script = + String("import bpy, sys;") + + "print('Blender 3.0 or higher is required.', file=sys.stderr) if bpy.app.version < (3, 0, 0) else None;" + + vformat("bpy.ops.wm.open_mainfile(filepath='%s');", source_global) + + unpack_all + + vformat("bpy.ops.export_scene.gltf(export_keep_originals=True,%s);", common_args); + print_verbose(script); + + // Run script with configured Blender binary. + + String blender_path = EDITOR_GET("filesystem/import/blender/blender3_path"); + +#ifdef WINDOWS_ENABLED + blender_path = blender_path.plus_file("blender.exe"); +#else + blender_path = blender_path.plus_file("blender"); +#endif + + List args; + args.push_back("--background"); + args.push_back("--python-expr"); + args.push_back(script); + + String standard_out; + int ret; + OS::get_singleton()->execute(blender_path, args, &standard_out, &ret, true); + print_verbose(blender_path); + print_verbose(standard_out); + + if (ret != 0) { + if (r_err) { + *r_err = ERR_SCRIPT_FAILED; + } + ERR_PRINT(vformat("Blend export to glTF failed with error: %d.", ret)); + return nullptr; + } + + // Import the generated glTF. + + // Use GLTFDocument instead of glTF importer to keep image references. + Ref gltf; + gltf.instantiate(); + Ref state; + state.instantiate(); + String base_dir; + if (p_options.has(SNAME("blender/materials/unpack_enabled")) && p_options[SNAME("blender/materials/unpack_enabled")]) { + base_dir = sink.get_base_dir(); + } + Error err = gltf->append_from_file(sink.get_basename() + ".gltf", state, p_flags, p_bake_fps, base_dir); + if (err != OK) { + if (r_err) { + *r_err = FAILED; + } + return nullptr; + } + return gltf->generate_scene(state, p_bake_fps); +} + +Variant EditorSceneFormatImporterBlend::get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option, + const HashMap &p_options) { + if (p_path.get_extension().to_lower() != "blend") { + return true; + } + + if (p_option.begins_with("animation/")) { + if (p_option != "animation/import" && !bool(p_options["animation/import"])) { + return false; + } + } + return true; +} + +void EditorSceneFormatImporterBlend::get_import_options(const String &p_path, List *r_options) { + if (p_path.get_extension().to_lower() != "blend") { + return; + } +#define ADD_OPTION_BOOL(PATH, VALUE) \ + r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, SNAME(PATH)), VALUE)); +#define ADD_OPTION_ENUM(PATH, ENUM_HINT, VALUE) \ + r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, SNAME(PATH), PROPERTY_HINT_ENUM, ENUM_HINT), VALUE)); + + ADD_OPTION_ENUM("blender/nodes/visible", "Visible Only,Renderable,All", BLEND_VISIBLE_ALL); + ADD_OPTION_BOOL("blender/nodes/punctual_lights", true); + ADD_OPTION_BOOL("blender/nodes/cameras", true); + ADD_OPTION_BOOL("blender/nodes/custom_properties", true); + ADD_OPTION_ENUM("blender/nodes/modifiers", "No Modifiers,All Modifiers", BLEND_MODIFIERS_ALL); + ADD_OPTION_BOOL("blender/meshes/colors", false); + ADD_OPTION_BOOL("blender/meshes/uvs", true); + ADD_OPTION_BOOL("blender/meshes/normals", true); + ADD_OPTION_BOOL("blender/meshes/tangents", true); + ADD_OPTION_ENUM("blender/meshes/skins", "None,4 Influences (Compatible),All Influences", BLEND_BONE_INFLUENCES_ALL); + ADD_OPTION_BOOL("blender/meshes/export_bones_deforming_mesh_only", false); + ADD_OPTION_BOOL("blender/materials/unpack_enabled", true); + ADD_OPTION_ENUM("blender/materials/export_materials", "Placeholder,Export", BLEND_MATERIAL_EXPORT_EXPORT); + ADD_OPTION_BOOL("blender/animation/limit_playback", true); + ADD_OPTION_BOOL("blender/animation/always_sample", true); + ADD_OPTION_BOOL("blender/animation/group_tracks", true); + +#undef ADD_OPTION_BOOL +#undef ADD_OPTION_ENUM +} + +/////////////////////////// + +static bool _test_blender_path(const String &p_path, String *r_err = nullptr) { + String path = p_path; +#ifdef WINDOWS_ENABLED + path = path.plus_file("blender.exe"); +#else + path = path.plus_file("blender"); +#endif + +#if defined(MACOS_ENABLED) + if (!FileAccess::exists(path)) { + path = path.plus_file("Blender"); + } +#endif + + if (!FileAccess::exists(path)) { + if (r_err) { + *r_err = TTR("Path does not contain a Blender installation."); + } + return false; + } + List args; + args.push_back("--version"); + String pipe; + Error err = OS::get_singleton()->execute(path, args, &pipe); + if (err != OK) { + if (r_err) { + *r_err = TTR("Can't execute Blender binary."); + } + return false; + } + + if (pipe.find("Blender ") != 0) { + if (r_err) { + *r_err = vformat(TTR("Unexpected --version output from Blender binary at: %s"), path); + } + return false; + } + pipe = pipe.replace_first("Blender ", ""); + int pp = pipe.find("."); + if (pp == -1) { + if (r_err) { + *r_err = TTR("Path supplied lacks a Blender binary."); + } + return false; + } + String v = pipe.substr(0, pp); + int version = v.to_int(); + if (version < 3) { + if (r_err) { + *r_err = TTR("This Blender installation is too old for this importer (not 3.0+)."); + } + return false; + } + if (version > 3) { + if (r_err) { + *r_err = TTR("This Blender installation is too new for this importer (not 3.x)."); + } + return false; + } + + return true; +} + +bool EditorFileSystemImportFormatSupportQueryBlend::is_active() const { + bool blend_enabled = GLOBAL_GET("filesystem/import/blender/enabled"); + + String blender_path = EDITOR_GET("filesystem/import/blender/blender3_path"); + + if (blend_enabled && !_test_blender_path(blender_path)) { + // Intending to import Blender, but blend not configured. + return true; + } + + return false; +} +Vector EditorFileSystemImportFormatSupportQueryBlend::get_file_extensions() const { + Vector ret; + ret.push_back("blend"); + return ret; +} + +void EditorFileSystemImportFormatSupportQueryBlend::_validate_path(String p_path) { + String error; + bool success = false; + if (p_path == "") { + error = TTR("Path is empty."); + } else { + if (_test_blender_path(p_path, &error)) { + success = true; + if (auto_detected_path == p_path) { + error = TTR("Path to Blender installation is valid (Autodetected)."); + } else { + error = TTR("Path to Blender installation is valid."); + } + } + } + + path_status->set_text(error); + + if (success) { + path_status->add_theme_color_override("font_color", path_status->get_theme_color(SNAME("success_color"), SNAME("Editor"))); + configure_blender_dialog->get_ok_button()->set_disabled(false); + } else { + path_status->add_theme_color_override("font_color", path_status->get_theme_color(SNAME("error_color"), SNAME("Editor"))); + configure_blender_dialog->get_ok_button()->set_disabled(true); + } +} + +bool EditorFileSystemImportFormatSupportQueryBlend::_autodetect_path(String p_path) { + if (_test_blender_path(p_path)) { + auto_detected_path = p_path; + return true; + } + return false; +} + +void EditorFileSystemImportFormatSupportQueryBlend::_path_confirmed() { + confirmed = true; +} + +void EditorFileSystemImportFormatSupportQueryBlend::_select_install(String p_path) { + blender_path->set_text(p_path); + _validate_path(p_path); +} +void EditorFileSystemImportFormatSupportQueryBlend::_browse_install() { + if (blender_path->get_text() != String()) { + browse_dialog->set_current_dir(blender_path->get_text()); + } + + browse_dialog->popup_centered_ratio(); +} + +bool EditorFileSystemImportFormatSupportQueryBlend::query() { + if (!configure_blender_dialog) { + configure_blender_dialog = memnew(ConfirmationDialog); + configure_blender_dialog->set_title(TTR("Configure Blender Importer")); + configure_blender_dialog->set_flag(Window::FLAG_BORDERLESS, true); // Avoid closing accidentally . + configure_blender_dialog->set_close_on_escape(false); + + VBoxContainer *vb = memnew(VBoxContainer); + vb->add_child(memnew(Label(TTR("Blender 3.0+ is required to import '.blend' files.\nPlease provide a valid path to a Blender installation:")))); + + HBoxContainer *hb = memnew(HBoxContainer); + + blender_path = memnew(LineEdit); + blender_path->set_h_size_flags(Control::SIZE_EXPAND_FILL); + hb->add_child(blender_path); + blender_path_browse = memnew(Button); + hb->add_child(blender_path_browse); + blender_path_browse->set_text(TTR("Browse")); + blender_path_browse->connect("pressed", callable_mp(this, &EditorFileSystemImportFormatSupportQueryBlend::_browse_install)); + hb->set_h_size_flags(Control::SIZE_EXPAND_FILL); + hb->set_custom_minimum_size(Size2(400 * EDSCALE, 0)); + + vb->add_child(hb); + + path_status = memnew(Label); + vb->add_child(path_status); + + configure_blender_dialog->add_child(vb); + + blender_path->connect("text_changed", callable_mp(this, &EditorFileSystemImportFormatSupportQueryBlend::_validate_path)); + + EditorNode::get_singleton()->get_gui_base()->add_child(configure_blender_dialog); + + configure_blender_dialog->set_ok_button_text(TTR("Confirm Path")); + configure_blender_dialog->set_cancel_button_text(TTR("Disable '.blend' Import")); + configure_blender_dialog->get_cancel_button()->set_tooltip(TTR("Disables Blender '.blend' files import for this project. Can be re-enabled in Project Settings.")); + configure_blender_dialog->connect("confirmed", callable_mp(this, &EditorFileSystemImportFormatSupportQueryBlend::_path_confirmed)); + + browse_dialog = memnew(EditorFileDialog); + browse_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM); + browse_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_DIR); + browse_dialog->connect("dir_selected", callable_mp(this, &EditorFileSystemImportFormatSupportQueryBlend::_select_install)); + + EditorNode::get_singleton()->get_gui_base()->add_child(browse_dialog); + } + + String path = EDITOR_GET("filesystem/import/blender/blender3_path"); + + if (path == "") { + // Autodetect + auto_detected_path = ""; + +#if defined(MACOS_ENABLED) + + { + Vector mdfind_paths; + { + List mdfind_args; + mdfind_args.push_back("kMDItemCFBundleIdentifier=org.blenderfoundation.blender"); + + String output; + Error err = OS::get_singleton()->execute("mdfind", mdfind_args, &output); + if (err == OK) { + mdfind_paths = output.split("\n"); + } + } + + bool found = false; + for (const String &path : mdfind_paths) { + found = _autodetect_path(path.plus_file("Contents/MacOS")); + if (found) { + break; + } + } + if (!found) { + found = _autodetect_path("/opt/homebrew/bin"); + } + if (!found) { + found = _autodetect_path("/opt/local/bin"); + } + if (!found) { + found = _autodetect_path("/usr/local/bin"); + } + if (!found) { + found = _autodetect_path("/usr/local/opt"); + } + if (!found) { + found = _autodetect_path("/Applications/Blender.app/Contents/MacOS"); + } + } +#elif defined(WINDOWS_ENABLED) + { + char blender_opener_path[MAX_PATH]; + DWORD path_len = MAX_PATH; + HRESULT res = AssocQueryString(0, ASSOCSTR_EXECUTABLE, ".blend", "open", blender_opener_path, &path_len); + if (res == S_OK && _autodetect_path(String(blender_opener_path).get_base_dir())) { + // Good. + } else if (_autodetect_path("C:\\Program Files\\Blender Foundation")) { + // Good. + } else { + _autodetect_path("C:\\Program Files (x86)\\Blender Foundation"); + } + } + +#elif defined(UNIX_ENABLED) + if (_autodetect_path("/usr/bin")) { + // Good. + } else if (_autodetect_path("/usr/local/bin")) { + // Good + } else { + _autodetect_path("/opt/blender/bin"); + } +#endif + if (auto_detected_path != "") { + path = auto_detected_path; + } + } + + blender_path->set_text(path); + + _validate_path(path); + + configure_blender_dialog->popup_centered(); + confirmed = false; + + while (true) { + OS::get_singleton()->delay_usec(1); + DisplayServer::get_singleton()->process_events(); + Main::iteration(); + if (!configure_blender_dialog->is_visible() || confirmed) { + break; + } + } + + if (confirmed) { + // Can only confirm a valid path. + EditorSettings::get_singleton()->set("filesystem/import/blender/blender3_path", blender_path->get_text()); + EditorSettings::get_singleton()->save(); + } else { + // Disable Blender import + ProjectSettings::get_singleton()->set("filesystem/import/blender/enabled", false); + ProjectSettings::get_singleton()->save(); + + if (EditorNode::immediate_confirmation_dialog(TTR("Disabling '.blend' file import requires restarting the editor."), TTR("Save & Restart"), TTR("Restart"))) { + EditorNode::get_singleton()->save_all_scenes(); + } + EditorNode::get_singleton()->restart_editor(); + return true; + } + + return false; +} + +EditorFileSystemImportFormatSupportQueryBlend::EditorFileSystemImportFormatSupportQueryBlend() { +} + +#endif // TOOLS_ENABLED diff --git a/modules/gltf/editor/editor_scene_importer_blend.h b/modules/gltf/editor/editor_scene_importer_blend.h new file mode 100644 index 000000000..dd1c1b988 --- /dev/null +++ b/modules/gltf/editor/editor_scene_importer_blend.h @@ -0,0 +1,112 @@ +/*************************************************************************/ +/* editor_scene_importer_blend.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (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_BLEND_H +#define EDITOR_SCENE_IMPORTER_BLEND_H + +#ifdef TOOLS_ENABLED + +#include "editor/editor_file_system.h" +#include "editor/import/resource_importer_scene.h" + +class Animation; +class Node; +class ConfirmationDialog; + +class EditorSceneFormatImporterBlend : public EditorSceneFormatImporter { + GDCLASS(EditorSceneFormatImporterBlend, EditorSceneFormatImporter); + +public: + enum { + BLEND_VISIBLE_VISIBLE_ONLY, + BLEND_VISIBLE_RENDERABLE, + BLEND_VISIBLE_ALL + }; + enum { + BLEND_BONE_INFLUENCES_NONE, + BLEND_BONE_INFLUENCES_COMPATIBLE, + BLEND_BONE_INFLUENCES_ALL + }; + enum { + BLEND_MATERIAL_EXPORT_PLACEHOLDER, + BLEND_MATERIAL_EXPORT_EXPORT + }; + enum { + BLEND_MODIFIERS_NONE, + BLEND_MODIFIERS_ALL + }; + + virtual uint32_t get_import_flags() const override; + virtual void get_extensions(List *r_extensions) const override; + virtual Node *import_scene(const String &p_path, uint32_t p_flags, + const HashMap &p_options, int p_bake_fps, + List *r_missing_deps, Error *r_err = nullptr) override; + virtual void get_import_options(const String &p_path, + List *r_options) override; + virtual Variant get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option, + const HashMap &p_options) override; +}; + +class LineEdit; +class Button; +class EditorFileDialog; +class Label; + +class EditorFileSystemImportFormatSupportQueryBlend : public EditorFileSystemImportFormatSupportQuery { + GDCLASS(EditorFileSystemImportFormatSupportQueryBlend, EditorFileSystemImportFormatSupportQuery); + + ConfirmationDialog *configure_blender_dialog = nullptr; + LineEdit *blender_path = nullptr; + Button *blender_path_browse = nullptr; + EditorFileDialog *browse_dialog = nullptr; + Label *path_status = nullptr; + bool confirmed = false; + + String auto_detected_path; + void _validate_path(String p_path); + + bool _autodetect_path(String p_path); + + void _path_confirmed(); + + void _select_install(String p_path); + void _browse_install(); + +public: + virtual bool is_active() const override; + virtual Vector get_file_extensions() const override; + virtual bool query() override; + + EditorFileSystemImportFormatSupportQueryBlend(); +}; + +#endif // TOOLS_ENABLED + +#endif // EDITOR_SCENE_IMPORTER_BLEND_H diff --git a/modules/gltf/editor/editor_scene_importer_fbx.cpp b/modules/gltf/editor/editor_scene_importer_fbx.cpp new file mode 100644 index 000000000..faad2d315 --- /dev/null +++ b/modules/gltf/editor/editor_scene_importer_fbx.cpp @@ -0,0 +1,117 @@ +/*************************************************************************/ +/* editor_scene_importer_fbx.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (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" + +#ifdef TOOLS_ENABLED + +#include "../gltf_document.h" +#include "../gltf_state.h" + +#include "core/config/project_settings.h" +#include "editor/editor_settings.h" +#include "scene/main/node.h" +#include "scene/resources/animation.h" + +uint32_t EditorSceneFormatImporterFBX::get_import_flags() const { + return ImportFlags::IMPORT_SCENE | ImportFlags::IMPORT_ANIMATION; +} + +void EditorSceneFormatImporterFBX::get_extensions(List *r_extensions) const { + r_extensions->push_back("fbx"); +} + +Node *EditorSceneFormatImporterFBX::import_scene(const String &p_path, uint32_t p_flags, + const HashMap &p_options, int p_bake_fps, + List *r_missing_deps, Error *r_err) { + // Get global paths for source and sink. + + // Don't use `c_escape()` as it can generate broken paths. These paths will be + // enclosed in double quotes by OS::execute(), so we only need to escape those. + // `c_escape_multiline()` seems to do this (escapes `\` and `"` only). + const String source_global = ProjectSettings::get_singleton()->globalize_path(p_path).c_escape_multiline(); + const String sink = ProjectSettings::get_singleton()->get_imported_files_path().plus_file( + vformat("%s-%s.glb", p_path.get_file().get_basename(), p_path.md5_text())); + const String sink_global = ProjectSettings::get_singleton()->globalize_path(sink).c_escape_multiline(); + + // Run fbx2gltf. + + String fbx2gltf_path = EDITOR_GET("filesystem/import/fbx/fbx2gltf_path"); + + List args; + args.push_back("--pbr-metallic-roughness"); + args.push_back("--input"); + args.push_back(source_global); + args.push_back("--output"); + args.push_back(sink_global); + args.push_back("--binary"); + + String standard_out; + int ret; + OS::get_singleton()->execute(fbx2gltf_path, args, &standard_out, &ret, true); + print_verbose(fbx2gltf_path); + print_verbose(standard_out); + + if (ret != 0) { + if (r_err) { + *r_err = ERR_SCRIPT_FAILED; + } + ERR_PRINT(vformat("FBX conversion to glTF failed with error: %d.", ret)); + return nullptr; + } + + // Import the generated glTF. + + // Use GLTFDocument instead of glTF importer to keep image references. + Ref gltf; + gltf.instantiate(); + Ref state; + state.instantiate(); + print_verbose(vformat("glTF path: %s", sink)); + Error err = gltf->append_from_file(sink, state, p_flags, p_bake_fps); + if (err != OK) { + if (r_err) { + *r_err = FAILED; + } + return nullptr; + } + return gltf->generate_scene(state, p_bake_fps); +} + +Variant EditorSceneFormatImporterFBX::get_option_visibility(const String &p_path, bool p_for_animation, + const String &p_option, const HashMap &p_options) { + return true; +} + +void EditorSceneFormatImporterFBX::get_import_options(const String &p_path, + List *r_options) { +} + +#endif // TOOLS_ENABLED diff --git a/modules/gltf/packed_scene_gltf.h b/modules/gltf/editor/editor_scene_importer_fbx.h similarity index 65% rename from modules/gltf/packed_scene_gltf.h rename to modules/gltf/editor/editor_scene_importer_fbx.h index dc0960d49..b0039b1c8 100644 --- a/modules/gltf/packed_scene_gltf.h +++ b/modules/gltf/editor/editor_scene_importer_fbx.h @@ -1,7 +1,5 @@ -#ifndef PACKED_SCENE_GLTF_H -#define PACKED_SCENE_GLTF_H /*************************************************************************/ -/* packed_scene_gltf.h */ +/* editor_scene_importer_fbx.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -30,36 +28,31 @@ /* 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 "scene/main/node.h" -#include "scene/resources/packed_scene.h" +#include "editor/import/resource_importer_scene.h" -#include "gltf_state.h" +class Animation; +class Node; -class PackedSceneGLTF : public PackedScene { - GDCLASS(PackedSceneGLTF, PackedScene); - -protected: - static void _bind_methods(); +class EditorSceneFormatImporterFBX : public EditorSceneFormatImporter { + GDCLASS(EditorSceneFormatImporterFBX, EditorSceneFormatImporter); 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 *r_missing_deps, Error *r_err = nullptr); - virtual void _build_parent_hierachy(Ref state); - virtual Error export_gltf(Node *p_root, String p_path, int32_t p_flags = 0, - real_t p_bake_fps = 1000.0f); + virtual uint32_t get_import_flags() const override; + virtual void get_extensions(List *r_extensions) const override; virtual Node *import_scene(const String &p_path, uint32_t p_flags, - int p_bake_fps, uint32_t p_compress_flags, - List *r_missing_deps, - Error *r_err, - Ref r_state); - virtual Node *import_gltf_scene(const String &p_path, uint32_t p_flags, float p_bake_fps, uint32_t p_compress_flags, Ref r_state = Ref()); - virtual void pack_gltf(String p_path, int32_t p_flags = 0, - real_t p_bake_fps = 1000.0f, uint32_t p_compress_flags = Mesh::ARRAY_COMPRESS_DEFAULT, Ref r_state = Ref()); + const HashMap &p_options, int p_bake_fps, + List *r_missing_deps, Error *r_err = nullptr) override; + virtual void get_import_options(const String &p_path, + List *r_options) override; + virtual Variant get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option, + const HashMap &p_options) override; }; -#endif // PACKED_SCENE_GLTF_H - #endif // TOOLS_ENABLED + +#endif // EDITOR_SCENE_IMPORTER_FBX_H diff --git a/modules/gltf/editor_scene_importer_gltf.cpp b/modules/gltf/editor/editor_scene_importer_gltf.cpp similarity index 77% rename from modules/gltf/editor_scene_importer_gltf.cpp rename to modules/gltf/editor/editor_scene_importer_gltf.cpp index af4bc9261..3fadec516 100644 --- a/modules/gltf/editor_scene_importer_gltf.cpp +++ b/modules/gltf/editor/editor_scene_importer_gltf.cpp @@ -31,34 +31,36 @@ #ifdef TOOLS_ENABLED #include "editor_scene_importer_gltf.h" + +#include "../gltf_document.h" +#include "../gltf_state.h" + #include "scene/resources/animation.h" -#include "scene/resources/skin.h" -#include "gltf_state.h" -#include "packed_scene_gltf.h" - -uint32_t EditorSceneImporterGLTF::get_import_flags() const { +uint32_t EditorSceneFormatImporterGLTF::get_import_flags() const { return ImportFlags::IMPORT_SCENE | ImportFlags::IMPORT_ANIMATION; } -void EditorSceneImporterGLTF::get_extensions(List *r_extensions) const { +void EditorSceneFormatImporterGLTF::get_extensions(List *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, uint32_t p_compress_flags, - List *r_missing_deps, - Error *r_err) { - Ref importer; - importer.instance(); - return importer->import_scene(p_path, p_flags, p_bake_fps, p_compress_flags, r_missing_deps, r_err, Ref()); -} - -Ref EditorSceneImporterGLTF::import_animation(const String &p_path, - uint32_t p_flags, - int p_bake_fps) { - return Ref(); +Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, uint32_t p_flags, + const HashMap &p_options, int p_bake_fps, + List *r_missing_deps, Error *r_err) { + Ref doc; + doc.instantiate(); + Ref state; + state.instantiate(); + Error err = doc->append_from_file(p_path, state, p_flags, p_bake_fps); + if (err != OK) { + if (r_err) { + *r_err = err; + } + return nullptr; + } + return doc->generate_scene(state, p_bake_fps); } #endif // TOOLS_ENABLED diff --git a/modules/gltf/editor_scene_importer_gltf.h b/modules/gltf/editor/editor_scene_importer_gltf.h similarity index 83% rename from modules/gltf/editor_scene_importer_gltf.h rename to modules/gltf/editor/editor_scene_importer_gltf.h index dc222cc84..b17a1e4ea 100644 --- a/modules/gltf/editor_scene_importer_gltf.h +++ b/modules/gltf/editor/editor_scene_importer_gltf.h @@ -1,5 +1,3 @@ -#ifndef EDITOR_SCENE_IMPORTER_GLTF_H -#define EDITOR_SCENE_IMPORTER_GLTF_H /*************************************************************************/ /* editor_scene_importer_gltf.h */ /*************************************************************************/ @@ -30,27 +28,30 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#ifndef EDITOR_SCENE_IMPORTER_GLTF_H +#define EDITOR_SCENE_IMPORTER_GLTF_H + #ifdef TOOLS_ENABLED +#include "../gltf_document_extension.h" +#include "../gltf_state.h" + #include "editor/import/resource_importer_scene.h" -#include "gltf_document.h" -#include "gltf_state.h" +class Animation; +class Node; -class EditorSceneImporterGLTF : public EditorSceneImporter { - GDCLASS(EditorSceneImporterGLTF, EditorSceneImporter); +class EditorSceneFormatImporterGLTF : public EditorSceneFormatImporter { + GDCLASS(EditorSceneFormatImporterGLTF, EditorSceneFormatImporter); public: - virtual uint32_t get_import_flags() const; - virtual void get_extensions(List *r_extensions) const; + virtual uint32_t get_import_flags() const override; + virtual void get_extensions(List *r_extensions) const override; virtual Node *import_scene(const String &p_path, uint32_t p_flags, - int p_bake_fps, uint32_t p_compress_flags, - List *r_missing_deps = nullptr, - Error *r_err = nullptr); - virtual Ref import_animation(const String &p_path, - uint32_t p_flags, int p_bake_fps); + const HashMap &p_options, int p_bake_fps, + List *r_missing_deps, Error *r_err = nullptr) override; }; -#endif // EDITOR_SCENE_IMPORTER_GLTF_H - #endif // TOOLS_ENABLED + +#endif // EDITOR_SCENE_IMPORTER_GLTF_H diff --git a/modules/gltf/gltf_light.cpp b/modules/gltf/extensions/gltf_light.cpp similarity index 82% rename from modules/gltf/gltf_light.cpp rename to modules/gltf/extensions/gltf_light.cpp index 05f0c7cc9..af21a4e80 100644 --- a/modules/gltf/gltf_light.cpp +++ b/modules/gltf/extensions/gltf_light.cpp @@ -35,8 +35,8 @@ void GLTFLight::_bind_methods() { 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_light_type"), &GLTFLight::get_light_type); + ClassDB::bind_method(D_METHOD("set_light_type", "light_type"), &GLTFLight::set_light_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); @@ -45,11 +45,11 @@ void GLTFLight::_bind_methods() { 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::REAL, "intensity"), "set_intensity", "get_intensity"); // float - ADD_PROPERTY(PropertyInfo(Variant::STRING, "type"), "set_type", "get_type"); // String - ADD_PROPERTY(PropertyInfo(Variant::REAL, "range"), "set_range", "get_range"); // float - ADD_PROPERTY(PropertyInfo(Variant::REAL, "inner_cone_angle"), "set_inner_cone_angle", "get_inner_cone_angle"); // float - ADD_PROPERTY(PropertyInfo(Variant::REAL, "outer_cone_angle"), "set_outer_cone_angle", "get_outer_cone_angle"); // float + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "intensity"), "set_intensity", "get_intensity"); // float + ADD_PROPERTY(PropertyInfo(Variant::STRING, "light_type"), "set_light_type", "get_light_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() { @@ -68,12 +68,12 @@ void GLTFLight::set_intensity(float p_intensity) { intensity = p_intensity; } -String GLTFLight::get_type() { - return type; +String GLTFLight::get_light_type() { + return light_type; } -void GLTFLight::set_type(String p_type) { - type = p_type; +void GLTFLight::set_light_type(String p_light_type) { + light_type = p_light_type; } float GLTFLight::get_range() { diff --git a/modules/gltf/gltf_light.h b/modules/gltf/extensions/gltf_light.h similarity index 94% rename from modules/gltf/gltf_light.h rename to modules/gltf/extensions/gltf_light.h index 5076917ed..58fa299df 100644 --- a/modules/gltf/gltf_light.h +++ b/modules/gltf/extensions/gltf_light.h @@ -1,5 +1,3 @@ -#ifndef GLTF_LIGHT_H -#define GLTF_LIGHT_H /*************************************************************************/ /* gltf_light.h */ /*************************************************************************/ @@ -30,7 +28,12 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "core/resource.h" +#ifndef GLTF_LIGHT_H +#define GLTF_LIGHT_H + +#include "core/config/engine.h" +#include "core/io/resource.h" +#include "scene/3d/light_3d.h" class GLTFLight : public Resource { GDCLASS(GLTFLight, Resource) @@ -42,7 +45,7 @@ protected: private: Color color = Color(1.0f, 1.0f, 1.0f); float intensity = 1.0f; - String type; + String light_type; float range = INFINITY; float inner_cone_angle = 0.0f; float outer_cone_angle = Math_TAU / 8.0f; @@ -54,8 +57,8 @@ public: float get_intensity(); void set_intensity(float p_intensity); - String get_type(); - void set_type(String p_type); + String get_light_type(); + void set_light_type(String p_light_type); float get_range(); void set_range(float p_range); diff --git a/modules/gltf/gltf_spec_gloss.cpp b/modules/gltf/extensions/gltf_spec_gloss.cpp similarity index 97% rename from modules/gltf/gltf_spec_gloss.cpp rename to modules/gltf/extensions/gltf_spec_gloss.cpp index ff688c5da..83af91bfc 100644 --- a/modules/gltf/gltf_spec_gloss.cpp +++ b/modules/gltf/extensions/gltf_spec_gloss.cpp @@ -44,7 +44,7 @@ void GLTFSpecGloss::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "diffuse_img"), "set_diffuse_img", "get_diffuse_img"); // Ref ADD_PROPERTY(PropertyInfo(Variant::COLOR, "diffuse_factor"), "set_diffuse_factor", "get_diffuse_factor"); // Color - ADD_PROPERTY(PropertyInfo(Variant::REAL, "gloss_factor"), "set_gloss_factor", "get_gloss_factor"); // float + 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 } diff --git a/modules/gltf/gltf_spec_gloss.h b/modules/gltf/extensions/gltf_spec_gloss.h similarity index 98% rename from modules/gltf/gltf_spec_gloss.h rename to modules/gltf/extensions/gltf_spec_gloss.h index 3a69f431c..a45fa4296 100644 --- a/modules/gltf/gltf_spec_gloss.h +++ b/modules/gltf/extensions/gltf_spec_gloss.h @@ -1,5 +1,3 @@ -#ifndef GLTF_SPEC_GLOSS_H -#define GLTF_SPEC_GLOSS_H /*************************************************************************/ /* gltf_spec_gloss.h */ /*************************************************************************/ @@ -30,8 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "core/image.h" -#include "core/resource.h" +#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); diff --git a/modules/gltf/gltf_defines.h b/modules/gltf/gltf_defines.h new file mode 100644 index 000000000..c20c87f79 --- /dev/null +++ b/modules/gltf/gltf_defines.h @@ -0,0 +1,87 @@ +/*************************************************************************/ +/* gltf_defines.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (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_DEFINES_H +#define GLTF_DEFINES_H + +// This file should only be included by other headers. + +// Godot classes used by GLTF headers. +class BoneAttachment3D; +class CSGShape3D; +class DirectionalLight3D; +class GridMap; +class Light3D; +class MultiMeshInstance3D; +class Skeleton3D; +class Skin; + +// GLTF classes. +struct GLTFAccessor; +class GLTFAnimation; +class GLTFBufferView; +class GLTFCamera; +class GLTFDocument; +class GLTFDocumentExtension; +class GLTFLight; +class GLTFMesh; +class GLTFNode; +class GLTFSkeleton; +class GLTFSkin; +class GLTFSpecGloss; +class GLTFState; +class GLTFTexture; + +// GLTF index aliases. +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; + +enum GLTFType { + TYPE_SCALAR, + TYPE_VEC2, + TYPE_VEC3, + TYPE_VEC4, + TYPE_MAT2, + TYPE_MAT3, + TYPE_MAT4, +}; + +#endif // GLTF_DEFINES_H diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index edcda55b1..7e90f198f 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -30,81 +30,99 @@ #include "gltf_document.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 "extensions/gltf_spec_gloss.h" +#include "gltf_document_extension.h" +#include "gltf_document_extension_convert_importer_mesh.h" #include "gltf_state.h" -#include "gltf_texture.h" -#include "core/bind/core_bind.h" // FIXME: Shouldn't use _Directory but DirAccess. #include "core/crypto/crypto_core.h" +#include "core/error/error_macros.h" +#include "core/io/dir_access.h" +#include "core/io/file_access.h" +#include "core/io/file_access_memory.h" #include "core/io/json.h" +#include "core/io/stream_peer.h" #include "core/math/disjoint_set.h" -#include "core/os/file_access.h" -#include "core/variant.h" +#include "core/math/vector2.h" +#include "core/variant/dictionary.h" +#include "core/variant/typed_array.h" +#include "core/variant/variant.h" #include "core/version.h" #include "drivers/png/png_driver_common.h" #include "scene/2d/node_2d.h" -#include "scene/3d/bone_attachment.h" -#include "scene/3d/mesh_instance.h" -#include "scene/3d/multimesh_instance.h" -#include "scene/3d/spatial.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/animation/animation_player.h" -#include "scene/main/node.h" -#include "scene/resources/animation.h" +#include "scene/resources/importer_mesh.h" +#include "scene/resources/material.h" +#include "scene/resources/mesh.h" #include "scene/resources/multimesh.h" -#include "scene/resources/skin.h" #include "scene/resources/surface_tool.h" -#include "modules/modules_enabled.gen.h" // For csg, gridmap, regex. +#include "modules/modules_enabled.gen.h" // For csg, gridmap. -#ifdef MODULE_REGEX_ENABLED -#include "modules/regex/regex.h" -#endif // MODULE_REGEX_ENABLED +#ifdef MODULE_CSG_ENABLED +#include "modules/csg/csg_shape.h" +#endif // MODULE_CSG_ENABLED +#ifdef MODULE_GRIDMAP_ENABLED +#include "modules/gridmap/grid_map.h" +#endif // MODULE_GRIDMAP_ENABLED -Ref _mesh_to_array_mesh(Ref p_mesh) { - Ref array_mesh = p_mesh; - if (array_mesh.is_valid()) { - return array_mesh; - } - array_mesh.instance(); +// FIXME: Hardcoded to avoid editor dependency. +#define GLTF_IMPORT_USE_NAMED_SKIN_BINDS 16 +#define GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS 32 + +#include +#include +#include +#include + +static Ref _mesh_to_importer_mesh(Ref p_mesh) { + Ref importer_mesh; + importer_mesh.instantiate(); if (p_mesh.is_null()) { - return array_mesh; + return importer_mesh; } + Ref array_mesh = p_mesh; + if (p_mesh->get_blend_shape_count()) { + ArrayMesh::BlendShapeMode shape_mode = ArrayMesh::BLEND_SHAPE_MODE_NORMALIZED; + if (array_mesh.is_valid()) { + shape_mode = array_mesh->get_blend_shape_mode(); + } + importer_mesh->set_blend_shape_mode(shape_mode); + for (int morph_i = 0; morph_i < p_mesh->get_blend_shape_count(); morph_i++) { + importer_mesh->add_blend_shape(p_mesh->get_blend_shape_name(morph_i)); + } + } for (int32_t surface_i = 0; surface_i < p_mesh->get_surface_count(); surface_i++) { - Mesh::PrimitiveType primitive_type = p_mesh->surface_get_primitive_type(surface_i); - Array arrays = p_mesh->surface_get_arrays(surface_i); + Array array = p_mesh->surface_get_arrays(surface_i); Ref mat = p_mesh->surface_get_material(surface_i); - int32_t mat_idx = array_mesh->get_surface_count(); - array_mesh->add_surface_from_arrays(primitive_type, arrays); - array_mesh->surface_set_material(mat_idx, mat); + String mat_name; + if (mat.is_valid()) { + mat_name = mat->get_name(); + } else { + // Assign default material when no material is assigned. + mat = Ref(memnew(StandardMaterial3D)); + } + importer_mesh->add_surface(p_mesh->surface_get_primitive_type(surface_i), + array, p_mesh->surface_get_blend_shape_arrays(surface_i), p_mesh->surface_get_lods(surface_i), mat, + mat_name, p_mesh->surface_get_format(surface_i)); } - - return array_mesh; + return importer_mesh; } -Error GLTFDocument::serialize(Ref state, Node *p_root, const String &p_path) { - uint64_t begin_time = OS::get_singleton()->get_ticks_usec(); - - state->skeleton3d_to_gltf_skeleton.clear(); - state->skin_and_skeleton3d_to_gltf_skin.clear(); - - _convert_scene_node(state, p_root, -1, -1); +Error GLTFDocument::_serialize(Ref state, const String &p_path) { if (!state->buffers.size()) { state->buffers.push_back(Vector()); } - /* STEP 1 CONVERT MESH INSTANCES */ + /* STEP CONVERT MESH INSTANCES */ _convert_mesh_instances(state); - /* STEP 2 SERIALIZE CAMERAS */ + /* STEP SERIALIZE CAMERAS */ Error err = _serialize_cameras(state); if (err != OK) { return Error::FAILED; @@ -116,37 +134,37 @@ Error GLTFDocument::serialize(Ref state, Node *p_root, const String & return Error::FAILED; } - /* STEP 5 SERIALIZE MESHES (we have enough info now) */ + /* STEP SERIALIZE MESHES (we have enough info now) */ err = _serialize_meshes(state); if (err != OK) { return Error::FAILED; } - /* STEP 6 SERIALIZE TEXTURES */ + /* STEP SERIALIZE TEXTURES */ err = _serialize_materials(state); if (err != OK) { return Error::FAILED; } - /* STEP 7 SERIALIZE ANIMATIONS */ + /* STEP SERIALIZE ANIMATIONS */ err = _serialize_animations(state); if (err != OK) { return Error::FAILED; } - /* STEP 8 SERIALIZE ACCESSORS */ + /* STEP SERIALIZE ACCESSORS */ err = _encode_accessors(state); if (err != OK) { return Error::FAILED; } - /* STEP 9 SERIALIZE IMAGES */ + /* STEP SERIALIZE IMAGES */ err = _serialize_images(state, p_path); if (err != OK) { return Error::FAILED; } - /* STEP 10 SERIALIZE TEXTURES */ + /* STEP SERIALIZE TEXTURES */ err = _serialize_textures(state); if (err != OK) { return Error::FAILED; @@ -156,65 +174,68 @@ Error GLTFDocument::serialize(Ref state, Node *p_root, const String & state->buffer_views.write[i]->buffer = 0; } - /* STEP 11 SERIALIZE BUFFER VIEWS */ + /* STEP SERIALIZE BUFFER VIEWS */ err = _encode_buffer_views(state); if (err != OK) { return Error::FAILED; } - /* STEP 12 SERIALIZE NODES */ + /* STEP SERIALIZE NODES */ err = _serialize_nodes(state); if (err != OK) { return Error::FAILED; } - /* STEP 13 SERIALIZE SCENE */ + /* STEP SERIALIZE SCENE */ err = _serialize_scenes(state); if (err != OK) { return Error::FAILED; } - /* STEP 14 SERIALIZE SCENE */ + /* STEP SERIALIZE SCENE */ err = _serialize_lights(state); if (err != OK) { return Error::FAILED; } - /* STEP 15 SERIALIZE EXTENSIONS */ + /* STEP SERIALIZE EXTENSIONS */ err = _serialize_extensions(state); if (err != OK) { return Error::FAILED; } - /* STEP 16 SERIALIZE VERSION */ + /* STEP 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; + for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { + Ref ext = document_extensions[ext_i]; + ERR_CONTINUE(ext.is_null()); + err = ext->export_post(state); + ERR_FAIL_COND_V(err != OK, err); } - uint64_t elapsed = OS::get_singleton()->get_ticks_usec() - begin_time; - float elapsed_sec = double(elapsed) / 1000000.0; - elapsed_sec = Math::stepify(elapsed_sec, 0.01f); - print_line("glTF: Export time elapsed seconds " + rtos(elapsed_sec).pad_decimals(2)); return OK; } Error GLTFDocument::_serialize_extensions(Ref 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; + if (!state->lights.is_empty()) { + extensions_used.push_back("KHR_lights_punctual"); + } + if (state->use_khr_texture_transform) { + extensions_used.push_back("KHR_texture_transform"); + extensions_required.push_back("KHR_texture_transform"); + } + if (!extensions_used.is_empty()) { + state->json["extensionsUsed"] = extensions_used; + } + if (!extensions_required.is_empty()) { + state->json["extensionsRequired"] = extensions_required; + } return OK; } @@ -225,7 +246,7 @@ Error GLTFDocument::_serialize_scenes(Ref state) { if (state->nodes.size()) { Dictionary s; - if (!state->scene_name.empty()) { + if (!state->scene_name.is_empty()) { s["name"] = state->scene_name; } @@ -241,42 +262,36 @@ Error GLTFDocument::_serialize_scenes(Ref state) { Error GLTFDocument::_parse_json(const String &p_path, Ref state) { Error err; - FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err); - if (!f) { + Ref f = FileAccess::open(p_path, FileAccess::READ, &err); + if (f.is_null()) { return err; } Vector array; - array.resize(f->get_len()); + array.resize(f->get_length()); 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); + JSON json; + err = json.parse(text); if (err != OK) { - _err_print_error("", p_path.utf8().get_data(), err_line, err_txt.utf8().get_data(), ERR_HANDLER_SCRIPT); + _err_print_error("", p_path.utf8().get_data(), json.get_error_line(), json.get_error_message().utf8().get_data(), false, ERR_HANDLER_SCRIPT); return err; } - state->json = v; + state->json = json.get_data(); return OK; } -Error GLTFDocument::_parse_glb(const String &p_path, Ref state) { - Error err; - FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err); - if (!f) { - return err; - } - +Error GLTFDocument::_parse_glb(Ref f, Ref state) { + ERR_FAIL_NULL_V(f, ERR_INVALID_PARAMETER); + ERR_FAIL_NULL_V(state, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(f->get_position() != 0, ERR_FILE_CANT_READ); 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(); @@ -289,16 +304,14 @@ Error GLTFDocument::_parse_glb(const String &p_path, Ref state) { 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); + JSON json; + Error err = json.parse(text); if (err != OK) { - _err_print_error("", p_path.utf8().get_data(), err_line, err_txt.utf8().get_data(), ERR_HANDLER_SCRIPT); + _err_print_error("", "", json.get_error_line(), json.get_error_message().utf8().get_data(), false, ERR_HANDLER_SCRIPT); return err; } - state->json = v; + state->json = json.get_data(); //data? @@ -332,47 +345,47 @@ static Vector3 _arr_to_vec3(const Array &p_array) { return Vector3(p_array[0], p_array[1], p_array[2]); } -static Array _quat_to_array(const Quat &p_quat) { +static Array _quaternion_to_array(const Quaternion &p_quaternion) { 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; + array[0] = p_quaternion.x; + array[1] = p_quaternion.y; + array[2] = p_quaternion.z; + array[3] = p_quaternion.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 Quaternion _arr_to_quaternion(const Array &p_array) { + ERR_FAIL_COND_V(p_array.size() != 4, Quaternion()); + return Quaternion(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()); +static Transform3D _arr_to_xform(const Array &p_array) { + ERR_FAIL_COND_V(p_array.size() != 16, Transform3D()); - 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])); + Transform3D xform; + xform.basis.set_column(Vector3::AXIS_X, Vector3(p_array[0], p_array[1], p_array[2])); + xform.basis.set_column(Vector3::AXIS_Y, Vector3(p_array[4], p_array[5], p_array[6])); + xform.basis.set_column(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 _xform_to_array(const Transform p_transform) { +static Vector _xform_to_array(const Transform3D p_transform) { Vector array; array.resize(16); - Vector3 axis_x = p_transform.get_basis().get_axis(Vector3::AXIS_X); + Vector3 axis_x = p_transform.get_basis().get_column(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); + Vector3 axis_y = p_transform.get_basis().get_column(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); + Vector3 axis_z = p_transform.get_basis().get_column(Vector3::AXIS_Z); array.write[8] = axis_z.x; array.write[9] = axis_z.y; array.write[10] = axis_z.z; @@ -392,7 +405,7 @@ Error GLTFDocument::_serialize_nodes(Ref state) { Ref n = state->nodes[i]; Dictionary extensions; node["extensions"] = extensions; - if (!n->get_name().empty()) { + if (!n->get_name().is_empty()) { node["name"] = n->get_name(); } if (n->camera != -1) { @@ -411,20 +424,20 @@ Error GLTFDocument::_serialize_nodes(Ref state) { } if (n->skeleton != -1 && n->skin < 0) { } - if (n->xform != Transform()) { + if (n->xform != Transform3D()) { node["matrix"] = _xform_to_array(n->xform); } - if (!n->rotation.is_equal_approx(Quat())) { - node["rotation"] = _quat_to_array(n->rotation); + if (!n->rotation.is_equal_approx(Quaternion())) { + node["rotation"] = _quaternion_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->position.is_equal_approx(Vector3())) { + node["translation"] = _vec3_to_arr(n->position); } if (n->children.size()) { Array children; @@ -433,37 +446,23 @@ Error GLTFDocument::_serialize_nodes(Ref state) { } node["children"] = children; } + + for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { + Ref ext = document_extensions[ext_i]; + ERR_CONTINUE(ext.is_null()); + ERR_CONTINUE(!state->scene_nodes.find(i)); + Error err = ext->export_node(state, n, state->json, state->scene_nodes[i]); + ERR_CONTINUE(err != OK); + } + nodes.push_back(node); } state->json["nodes"] = nodes; return OK; } -String GLTFDocument::_sanitize_scene_name(Ref state, const String &p_name) { - if (state->use_legacy_names) { -#ifdef MODULE_REGEX_ENABLED - RegEx regex("([^a-zA-Z0-9_ -]+)"); - String s_name = regex.sub(p_name, "", true); - return s_name; -#else - WARN_PRINT("GLTF: Legacy scene names are not supported without the RegEx module. Falling back to new names."); -#endif // MODULE_REGEX_ENABLED - } - return p_name.validate_node_name(); -} - -String GLTFDocument::_legacy_validate_node_name(const String &p_name) { - String invalid_character = ". : @ / \""; - String name = p_name; - Vector chars = invalid_character.split(" "); - for (int i = 0; i < chars.size(); i++) { - name = name.replace(chars[i], ""); - } - return name; -} - String GLTFDocument::_gen_unique_name(Ref state, const String &p_name) { - const String s_name = _sanitize_scene_name(state, p_name); + const String s_name = p_name.validate_node_name(); String name; int index = 1; @@ -471,9 +470,6 @@ String GLTFDocument::_gen_unique_name(Ref state, const String &p_name name = s_name; if (index > 1) { - if (state->use_legacy_names) { - name += " "; - } name += itos(index); } if (!state->unique_names.has(name)) { @@ -520,39 +516,18 @@ String GLTFDocument::_gen_unique_animation_name(Ref state, const Stri return name; } -String GLTFDocument::_sanitize_bone_name(Ref state, const String &p_name) { - if (state->use_legacy_names) { -#ifdef MODULE_REGEX_ENABLED - String name = p_name.camelcase_to_underscore(true); - RegEx pattern_del("([^a-zA-Z0-9_ ])+"); - - name = pattern_del.sub(name, "", true); - - RegEx pattern_nospace(" +"); - name = pattern_nospace.sub(name, "_", true); - - RegEx pattern_multiple("_+"); - name = pattern_multiple.sub(name, "_", true); - - RegEx pattern_padded("0+(\\d+)"); - name = pattern_padded.sub(name, "$1", true); - - return name; -#else - WARN_PRINT("GLTF: Legacy bone names are not supported without the RegEx module. Falling back to new names."); -#endif // MODULE_REGEX_ENABLED - } +String GLTFDocument::_sanitize_bone_name(const String &p_name) { String name = p_name; name = name.replace(":", "_"); name = name.replace("/", "_"); - if (name.empty()) { - name = "bone"; - } return name; } String GLTFDocument::_gen_unique_bone_name(Ref state, const GLTFSkeletonIndex skel_i, const String &p_name) { - String s_name = _sanitize_bone_name(state, 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) { @@ -591,10 +566,10 @@ Error GLTFDocument::_parse_scenes(Ref state) { state->root_nodes.push_back(nodes[j]); } - if (s.has("name") && !String(s["name"]).empty() && !((String)s["name"]).begins_with("Scene")) { - state->scene_name = s["name"]; + if (s.has("name") && !String(s["name"]).is_empty() && !((String)s["name"]).begins_with("Scene")) { + state->scene_name = _gen_unique_name(state, s["name"]); } else { - state->scene_name = state->filename; + state->scene_name = _gen_unique_name(state, state->filename); } } @@ -606,7 +581,7 @@ Error GLTFDocument::_parse_nodes(Ref state) { const Array &nodes = state->json["nodes"]; for (int i = 0; i < nodes.size(); i++) { Ref node; - node.instance(); + node.instantiate(); const Dictionary &n = nodes[i]; if (n.has("name")) { @@ -625,17 +600,17 @@ Error GLTFDocument::_parse_nodes(Ref state) { node->xform = _arr_to_xform(n["matrix"]); } else { if (n.has("translation")) { - node->translation = _arr_to_vec3(n["translation"]); + node->position = _arr_to_vec3(n["translation"]); } if (n.has("rotation")) { - node->rotation = _arr_to_quat(n["rotation"]); + node->rotation = _arr_to_quaternion(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; + node->xform.basis.set_quaternion_scale(node->rotation, node->scale); + node->xform.origin = node->position; } if (n.has("extensions")) { @@ -701,7 +676,7 @@ static Vector _parse_base64_uri(const String &uri) { int start = uri.find(","); ERR_FAIL_COND_V(start == -1, Vector()); - CharString substr = uri.right(start + 1).ascii(); + CharString substr = uri.substr(start + 1).ascii(); int strlen = substr.length(); @@ -736,8 +711,8 @@ Error GLTFDocument::_encode_buffer_glb(Ref state, const String &p_pat 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) { + Ref f = FileAccess::open(path, FileAccess::WRITE, &err); + if (f.is_null()) { return err; } if (buffer_data.size() == 0) { @@ -745,14 +720,10 @@ Error GLTFDocument::_encode_buffer_glb(Ref state, const String &p_pat } 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); } - if (!buffers.size()) { - return OK; - } state->json["buffers"] = buffers; return OK; @@ -772,8 +743,8 @@ Error GLTFDocument::_encode_buffer_bins(Ref state, const String &p_pa 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) { + Ref f = FileAccess::open(path, FileAccess::WRITE, &err); + if (f.is_null()) { return err; } if (buffer_data.size() == 0) { @@ -781,7 +752,6 @@ Error GLTFDocument::_encode_buffer_bins(Ref state, const String &p_pa } 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); @@ -815,7 +785,8 @@ Error GLTFDocument::_parse_buffers(Ref state, const String &p_base_pa } buffer_data = _parse_base64_uri(uri); } else { // Relative path to an external image file. - uri = uri.http_unescape(); + ERR_FAIL_COND_V(p_base_path.is_empty(), ERR_INVALID_PARAMETER); + uri = uri.uri_decode(); 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); @@ -874,7 +845,7 @@ Error GLTFDocument::_parse_buffer_views(Ref state) { const Dictionary &d = buffers[i]; Ref buffer_view; - buffer_view.instance(); + buffer_view.instantiate(); ERR_FAIL_COND_V(!d.has("buffer"), ERR_PARSE_ERROR); buffer_view->buffer = d["buffer"]; @@ -913,18 +884,8 @@ Error GLTFDocument::_encode_accessors(Ref state) { d["type"] = _get_accessor_type_name(accessor->type); d["byteOffset"] = accessor->byte_offset; d["normalized"] = accessor->normalized; - Array max; - max.resize(accessor->max.size()); - for (int32_t max_i = 0; max_i < max.size(); max_i++) { - max[max_i] = accessor->max[max_i]; - } - d["max"] = max; - Array min; - min.resize(accessor->min.size()); - for (int32_t min_i = 0; min_i < min.size(); min_i++) { - min[min_i] = accessor->min[min_i]; - } - d["min"] = min; + d["max"] = accessor->max; + d["min"] = accessor->min; d["bufferView"] = accessor->buffer_view; //optional because it may be sparse... // Dictionary s; @@ -970,58 +931,58 @@ Error GLTFDocument::_encode_accessors(Ref state) { return OK; } -String GLTFDocument::_get_accessor_type_name(const GLTFDocument::GLTFType p_type) { - if (p_type == GLTFDocument::TYPE_SCALAR) { +String GLTFDocument::_get_accessor_type_name(const GLTFType p_type) { + if (p_type == GLTFType::TYPE_SCALAR) { return "SCALAR"; } - if (p_type == GLTFDocument::TYPE_VEC2) { + if (p_type == GLTFType::TYPE_VEC2) { return "VEC2"; } - if (p_type == GLTFDocument::TYPE_VEC3) { + if (p_type == GLTFType::TYPE_VEC3) { return "VEC3"; } - if (p_type == GLTFDocument::TYPE_VEC4) { + if (p_type == GLTFType::TYPE_VEC4) { return "VEC4"; } - if (p_type == GLTFDocument::TYPE_MAT2) { + if (p_type == GLTFType::TYPE_MAT2) { return "MAT2"; } - if (p_type == GLTFDocument::TYPE_MAT3) { + if (p_type == GLTFType::TYPE_MAT3) { return "MAT3"; } - if (p_type == GLTFDocument::TYPE_MAT4) { + if (p_type == GLTFType::TYPE_MAT4) { return "MAT4"; } ERR_FAIL_V("SCALAR"); } -GLTFDocument::GLTFType GLTFDocument::_get_type_from_str(const String &p_string) { +GLTFType GLTFDocument::_get_type_from_str(const String &p_string) { if (p_string == "SCALAR") { - return GLTFDocument::TYPE_SCALAR; + return GLTFType::TYPE_SCALAR; } if (p_string == "VEC2") { - return GLTFDocument::TYPE_VEC2; + return GLTFType::TYPE_VEC2; } if (p_string == "VEC3") { - return GLTFDocument::TYPE_VEC3; + return GLTFType::TYPE_VEC3; } if (p_string == "VEC4") { - return GLTFDocument::TYPE_VEC4; + return GLTFType::TYPE_VEC4; } if (p_string == "MAT2") { - return GLTFDocument::TYPE_MAT2; + return GLTFType::TYPE_MAT2; } if (p_string == "MAT3") { - return GLTFDocument::TYPE_MAT3; + return GLTFType::TYPE_MAT3; } if (p_string == "MAT4") { - return GLTFDocument::TYPE_MAT4; + return GLTFType::TYPE_MAT4; } - ERR_FAIL_V(GLTFDocument::TYPE_SCALAR); + ERR_FAIL_V(GLTFType::TYPE_SCALAR); } Error GLTFDocument::_parse_accessors(Ref state) { @@ -1033,7 +994,7 @@ Error GLTFDocument::_parse_accessors(Ref state) { const Dictionary &d = accessors[i]; Ref accessor; - accessor.instance(); + accessor.instantiate(); ERR_FAIL_COND_V(!d.has("componentType"), ERR_PARSE_ERROR); accessor->component_type = d["componentType"]; @@ -1055,21 +1016,11 @@ Error GLTFDocument::_parse_accessors(Ref state) { } if (d.has("max")) { - Array max = d["max"]; - accessor->max.resize(max.size()); - PoolVector::Write max_write = accessor->max.write(); - for (int32_t max_i = 0; max_i < accessor->max.size(); max_i++) { - max_write[max_i] = max[max_i]; - } + accessor->max = d["max"]; } if (d.has("min")) { - Array min = d["min"]; - accessor->min.resize(min.size()); - PoolVector::Write min_write = accessor->min.write(); - for (int32_t min_i = 0; min_i < accessor->min.size(); min_i++) { - min_write[min_i] = min[min_i]; - } + accessor->min = d["min"]; } if (d.has("sparse")) { @@ -1185,7 +1136,7 @@ Error GLTFDocument::_encode_buffer_view(Ref state, const double *src, } Ref bv; - bv.instance(); + bv.instantiate(); const uint32_t offset = bv->byte_offset = byte_offset; Vector &gltf_buffer = state->buffers.write[0]; @@ -1561,7 +1512,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_ints(Ref state, c Vector type_min; type_min.resize(element_count); for (int i = 0; i < p_attribs.size(); i++) { - attribs.write[i] = Math::stepify(p_attribs[i], 1.0); + 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]; @@ -1579,26 +1530,14 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_ints(Ref state, c ERR_FAIL_COND_V(attribs.size() == 0, -1); Ref accessor; - accessor.instance(); + accessor.instantiate(); GLTFBufferIndex buffer_view_i; int64_t size = state->buffers[0].size(); - const GLTFDocument::GLTFType type = GLTFDocument::TYPE_SCALAR; + const GLTFType type = GLTFType::TYPE_SCALAR; const int component_type = GLTFDocument::COMPONENT_TYPE_INT; - PoolVector max; - max.resize(type_max.size()); - PoolVector::Write write_max = max.write(); - for (int32_t max_i = 0; max_i < max.size(); max_i++) { - write_max[max_i] = type_max[max_i]; - } - accessor->max = max; - PoolVector min; - min.resize(type_min.size()); - PoolVector::Write write_min = min.write(); - for (int32_t min_i = 0; min_i < min.size(); min_i++) { - write_min[min_i] = type_min[min_i]; - } - accessor->min = min; + accessor->max = type_max; + accessor->min = type_min; accessor->normalized = false; accessor->count = ret_size; accessor->type = type; @@ -1667,34 +1606,22 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec2(Ref state, c for (int i = 0; i < p_attribs.size(); i++) { Vector2 attrib = p_attribs[i]; - attribs.write[(i * element_count) + 0] = Math::stepify(attrib.x, CMP_NORMALIZE_TOLERANCE); - attribs.write[(i * element_count) + 1] = Math::stepify(attrib.y, CMP_NORMALIZE_TOLERANCE); + 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 accessor; - accessor.instance(); + accessor.instantiate(); GLTFBufferIndex buffer_view_i; int64_t size = state->buffers[0].size(); - const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC2; + const GLTFType type = GLTFType::TYPE_VEC2; const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT; - PoolVector max; - max.resize(type_max.size()); - PoolVector::Write write_max = max.write(); - for (int32_t max_i = 0; max_i < max.size(); max_i++) { - write_max[max_i] = type_max[max_i]; - } - accessor->max = max; - PoolVector min; - min.resize(type_min.size()); - PoolVector::Write write_min = min.write(); - for (int32_t min_i = 0; min_i < min.size(); min_i++) { - write_min[min_i] = type_min[min_i]; - } - accessor->min = min; + accessor->max = type_max; + accessor->min = type_min; accessor->normalized = false; accessor->count = p_attribs.size(); accessor->type = type; @@ -1725,10 +1652,10 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_color(Ref state, 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::stepify(attrib.r, CMP_NORMALIZE_TOLERANCE); - attribs.write[(i * element_count) + 1] = Math::stepify(attrib.g, CMP_NORMALIZE_TOLERANCE); - attribs.write[(i * element_count) + 2] = Math::stepify(attrib.b, CMP_NORMALIZE_TOLERANCE); - attribs.write[(i * element_count) + 3] = Math::stepify(attrib.a, CMP_NORMALIZE_TOLERANCE); + 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); } @@ -1736,25 +1663,14 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_color(Ref state, ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1); Ref accessor; - accessor.instance(); + accessor.instantiate(); GLTFBufferIndex buffer_view_i; int64_t size = state->buffers[0].size(); - const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC4; + const GLTFType type = GLTFType::TYPE_VEC4; const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT; - PoolVector max; - max.resize(type_max.size()); - PoolVector::Write write_max = max.write(); - for (int32_t max_i = 0; max_i < max.size(); max_i++) { - write_max[max_i] = type_max[max_i]; - } - accessor->max = max; - PoolVector min; - min.resize(type_min.size()); - PoolVector::Write write_min = min.write(); - for (int32_t min_i = 0; min_i < min.size(); min_i++) { - write_min[min_i] = type_min[min_i]; - } - accessor->min = min; + + accessor->max = type_max; + accessor->min = type_min; accessor->normalized = false; accessor->count = p_attribs.size(); accessor->type = type; @@ -1801,10 +1717,10 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_weights(Ref state 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::stepify(attrib.r, CMP_NORMALIZE_TOLERANCE); - attribs.write[(i * element_count) + 1] = Math::stepify(attrib.g, CMP_NORMALIZE_TOLERANCE); - attribs.write[(i * element_count) + 2] = Math::stepify(attrib.b, CMP_NORMALIZE_TOLERANCE); - attribs.write[(i * element_count) + 3] = Math::stepify(attrib.a, CMP_NORMALIZE_TOLERANCE); + 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); } @@ -1812,26 +1728,14 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_weights(Ref state ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1); Ref accessor; - accessor.instance(); + accessor.instantiate(); GLTFBufferIndex buffer_view_i; int64_t size = state->buffers[0].size(); - const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC4; + const GLTFType type = GLTFType::TYPE_VEC4; const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT; - PoolVector max; - max.resize(type_max.size()); - PoolVector::Write write_max = max.write(); - for (int32_t max_i = 0; max_i < max.size(); max_i++) { - write_max[max_i] = type_max[max_i]; - } - accessor->max = max; - PoolVector min; - min.resize(type_min.size()); - PoolVector::Write write_min = min.write(); - for (int32_t min_i = 0; min_i < min.size(); min_i++) { - write_min[min_i] = type_min[min_i]; - } - accessor->min = min; + accessor->max = type_max; + accessor->min = type_min; accessor->normalized = false; accessor->count = p_attribs.size(); accessor->type = type; @@ -1862,35 +1766,23 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_joints(Ref state, 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::stepify(attrib.r, CMP_NORMALIZE_TOLERANCE); - attribs.write[(i * element_count) + 1] = Math::stepify(attrib.g, CMP_NORMALIZE_TOLERANCE); - attribs.write[(i * element_count) + 2] = Math::stepify(attrib.b, CMP_NORMALIZE_TOLERANCE); - attribs.write[(i * element_count) + 3] = Math::stepify(attrib.a, CMP_NORMALIZE_TOLERANCE); + 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 accessor; - accessor.instance(); + accessor.instantiate(); GLTFBufferIndex buffer_view_i; int64_t size = state->buffers[0].size(); - const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC4; + const GLTFType type = GLTFType::TYPE_VEC4; const int component_type = GLTFDocument::COMPONENT_TYPE_UNSIGNED_SHORT; - PoolVector max; - max.resize(type_max.size()); - PoolVector::Write write_max = max.write(); - for (int32_t max_i = 0; max_i < max.size(); max_i++) { - write_max[max_i] = type_max[max_i]; - } - accessor->max = max; - PoolVector min; - min.resize(type_min.size()); - PoolVector::Write write_min = min.write(); - for (int32_t min_i = 0; min_i < min.size(); min_i++) { - write_min[min_i] = type_min[min_i]; - } - accessor->min = min; + accessor->max = type_max; + accessor->min = type_min; accessor->normalized = false; accessor->count = p_attribs.size(); accessor->type = type; @@ -1905,7 +1797,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_joints(Ref state, return state->accessors.size() - 1; } -GLTFAccessorIndex GLTFDocument::_encode_accessor_as_quats(Ref state, const Vector p_attribs, const bool p_for_vertex) { +GLTFAccessorIndex GLTFDocument::_encode_accessor_as_quaternions(Ref state, const Vector p_attribs, const bool p_for_vertex) { if (p_attribs.size() == 0) { return -1; } @@ -1920,11 +1812,11 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_quats(Ref state, Vector 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::stepify(quat.x, CMP_NORMALIZE_TOLERANCE); - attribs.write[(i * element_count) + 1] = Math::stepify(quat.y, CMP_NORMALIZE_TOLERANCE); - attribs.write[(i * element_count) + 2] = Math::stepify(quat.z, CMP_NORMALIZE_TOLERANCE); - attribs.write[(i * element_count) + 3] = Math::stepify(quat.w, CMP_NORMALIZE_TOLERANCE); + Quaternion quaternion = p_attribs[i]; + attribs.write[(i * element_count) + 0] = Math::snapped(quaternion.x, CMP_NORMALIZE_TOLERANCE); + attribs.write[(i * element_count) + 1] = Math::snapped(quaternion.y, CMP_NORMALIZE_TOLERANCE); + attribs.write[(i * element_count) + 2] = Math::snapped(quaternion.z, CMP_NORMALIZE_TOLERANCE); + attribs.write[(i * element_count) + 3] = Math::snapped(quaternion.w, CMP_NORMALIZE_TOLERANCE); _calc_accessor_min_max(i, element_count, type_max, attribs, type_min); } @@ -1932,26 +1824,14 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_quats(Ref state, ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1); Ref accessor; - accessor.instance(); + accessor.instantiate(); GLTFBufferIndex buffer_view_i; int64_t size = state->buffers[0].size(); - const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC4; + const GLTFType type = GLTFType::TYPE_VEC4; const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT; - PoolVector max; - max.resize(type_max.size()); - PoolVector::Write write_max = max.write(); - for (int32_t max_i = 0; max_i < max.size(); max_i++) { - write_max[max_i] = type_max[max_i]; - } - accessor->max = max; - PoolVector min; - min.resize(type_min.size()); - PoolVector::Write write_min = min.write(); - for (int32_t min_i = 0; min_i < min.size(); min_i++) { - write_min[min_i] = type_min[min_i]; - } - accessor->min = min; + accessor->max = type_max; + accessor->min = type_min; accessor->normalized = false; accessor->count = p_attribs.size(); accessor->type = type; @@ -2001,7 +1881,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_floats(Ref state, type_min.resize(element_count); for (int i = 0; i < p_attribs.size(); i++) { - attribs.write[i] = Math::stepify(p_attribs[i], CMP_NORMALIZE_TOLERANCE); + attribs.write[i] = Math::snapped(p_attribs[i], CMP_NORMALIZE_TOLERANCE); _calc_accessor_min_max(i, element_count, type_max, attribs, type_min); } @@ -2009,26 +1889,14 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_floats(Ref state, ERR_FAIL_COND_V(!attribs.size(), -1); Ref accessor; - accessor.instance(); + accessor.instantiate(); GLTFBufferIndex buffer_view_i; int64_t size = state->buffers[0].size(); - const GLTFDocument::GLTFType type = GLTFDocument::TYPE_SCALAR; + const GLTFType type = GLTFType::TYPE_SCALAR; const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT; - PoolVector max; - max.resize(type_max.size()); - PoolVector::Write write_max = max.write(); - for (int32_t max_i = 0; max_i < max.size(); max_i++) { - write_max[max_i] = type_max[max_i]; - } - accessor->max = max; - PoolVector min; - min.resize(type_min.size()); - PoolVector::Write write_min = min.write(); - for (int32_t min_i = 0; min_i < min.size(); min_i++) { - write_min[min_i] = type_min[min_i]; - } - accessor->min = min; + accessor->max = type_max; + accessor->min = type_min; accessor->normalized = false; accessor->count = ret_size; accessor->type = type; @@ -2058,35 +1926,23 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec3(Ref state, c 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::stepify(attrib.x, CMP_NORMALIZE_TOLERANCE); - attribs.write[(i * element_count) + 1] = Math::stepify(attrib.y, CMP_NORMALIZE_TOLERANCE); - attribs.write[(i * element_count) + 2] = Math::stepify(attrib.z, CMP_NORMALIZE_TOLERANCE); + 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 accessor; - accessor.instance(); + accessor.instantiate(); GLTFBufferIndex buffer_view_i; int64_t size = state->buffers[0].size(); - const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC3; + const GLTFType type = GLTFType::TYPE_VEC3; const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT; - PoolVector max; - max.resize(type_max.size()); - PoolVector::Write write_max = max.write(); - for (int32_t max_i = 0; max_i < max.size(); max_i++) { - write_max[max_i] = type_max[max_i]; - } - accessor->max = max; - PoolVector min; - min.resize(type_min.size()); - PoolVector::Write write_min = min.write(); - for (int32_t min_i = 0; min_i < min.size(); min_i++) { - write_min[min_i] = type_min[min_i]; - } - accessor->min = min; + accessor->max = type_max; + accessor->min = type_min; accessor->normalized = false; accessor->count = p_attribs.size(); accessor->type = type; @@ -2101,7 +1957,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec3(Ref state, c return state->accessors.size() - 1; } -GLTFAccessorIndex GLTFDocument::_encode_accessor_as_xform(Ref state, const Vector p_attribs, const bool p_for_vertex) { +GLTFAccessorIndex GLTFDocument::_encode_accessor_as_xform(Ref state, const Vector p_attribs, const bool p_for_vertex) { if (p_attribs.size() == 0) { return -1; } @@ -2115,31 +1971,31 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_xform(Ref state, Vector type_min; type_min.resize(element_count); for (int i = 0; i < p_attribs.size(); i++) { - Transform attrib = p_attribs[i]; + Transform3D attrib = p_attribs[i]; Basis basis = attrib.get_basis(); - Vector3 axis_0 = basis.get_axis(Vector3::AXIS_X); + Vector3 axis_0 = basis.get_column(Vector3::AXIS_X); - attribs.write[i * element_count + 0] = Math::stepify(axis_0.x, CMP_NORMALIZE_TOLERANCE); - attribs.write[i * element_count + 1] = Math::stepify(axis_0.y, CMP_NORMALIZE_TOLERANCE); - attribs.write[i * element_count + 2] = Math::stepify(axis_0.z, CMP_NORMALIZE_TOLERANCE); + 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::stepify(axis_1.x, CMP_NORMALIZE_TOLERANCE); - attribs.write[i * element_count + 5] = Math::stepify(axis_1.y, CMP_NORMALIZE_TOLERANCE); - attribs.write[i * element_count + 6] = Math::stepify(axis_1.z, CMP_NORMALIZE_TOLERANCE); + Vector3 axis_1 = basis.get_column(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::stepify(axis_2.x, CMP_NORMALIZE_TOLERANCE); - attribs.write[i * element_count + 9] = Math::stepify(axis_2.y, CMP_NORMALIZE_TOLERANCE); - attribs.write[i * element_count + 10] = Math::stepify(axis_2.z, CMP_NORMALIZE_TOLERANCE); + Vector3 axis_2 = basis.get_column(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::stepify(origin.x, CMP_NORMALIZE_TOLERANCE); - attribs.write[i * element_count + 13] = Math::stepify(origin.y, CMP_NORMALIZE_TOLERANCE); - attribs.write[i * element_count + 14] = Math::stepify(origin.z, CMP_NORMALIZE_TOLERANCE); + 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); @@ -2147,26 +2003,14 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_xform(Ref state, ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1); Ref accessor; - accessor.instance(); + accessor.instantiate(); GLTFBufferIndex buffer_view_i; int64_t size = state->buffers[0].size(); - const GLTFDocument::GLTFType type = GLTFDocument::TYPE_MAT4; + const GLTFType type = GLTFType::TYPE_MAT4; const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT; - PoolVector max; - max.resize(type_max.size()); - PoolVector::Write write_max = max.write(); - for (int32_t max_i = 0; max_i < max.size(); max_i++) { - write_max[max_i] = type_max[max_i]; - } - accessor->max = max; - PoolVector min; - min.resize(type_min.size()); - PoolVector::Write write_min = min.write(); - for (int32_t min_i = 0; min_i < min.size(); min_i++) { - write_min[min_i] = type_min[min_i]; - } - accessor->min = min; + accessor->max = type_max; + accessor->min = type_min; accessor->normalized = false; accessor->count = p_attribs.size(); accessor->type = type; @@ -2227,9 +2071,9 @@ Vector GLTFDocument::_decode_accessor_as_color(Ref state, cons } return ret; } -Vector GLTFDocument::_decode_accessor_as_quat(Ref state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) { +Vector GLTFDocument::_decode_accessor_as_quaternion(Ref state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) { const Vector attribs = _decode_accessor(state, p_accessor, p_for_vertex); - Vector ret; + Vector ret; if (attribs.size() == 0) { return ret; @@ -2241,7 +2085,7 @@ Vector GLTFDocument::_decode_accessor_as_quat(Ref state, const 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(); + ret.write[i] = Quaternion(attribs_ptr[i * 4 + 0], attribs_ptr[i * 4 + 1], attribs_ptr[i * 4 + 2], attribs_ptr[i * 4 + 3]).normalized(); } } return ret; @@ -2274,16 +2118,16 @@ Vector GLTFDocument::_decode_accessor_as_basis(Ref state, cons 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])); + ret.write[i].set_column(0, Vector3(attribs[i * 9 + 0], attribs[i * 9 + 1], attribs[i * 9 + 2])); + ret.write[i].set_column(1, Vector3(attribs[i * 9 + 3], attribs[i * 9 + 4], attribs[i * 9 + 5])); + ret.write[i].set_column(2, Vector3(attribs[i * 9 + 6], attribs[i * 9 + 7], attribs[i * 9 + 8])); } return ret; } -Vector GLTFDocument::_decode_accessor_as_xform(Ref state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) { +Vector GLTFDocument::_decode_accessor_as_xform(Ref state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) { const Vector attribs = _decode_accessor(state, p_accessor, p_for_vertex); - Vector ret; + Vector ret; if (attribs.size() == 0) { return ret; @@ -2292,9 +2136,9 @@ Vector GLTFDocument::_decode_accessor_as_xform(Ref state, 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].basis.set_column(0, Vector3(attribs[i * 16 + 0], attribs[i * 16 + 1], attribs[i * 16 + 2])); + ret.write[i].basis.set_column(1, Vector3(attribs[i * 16 + 4], attribs[i * 16 + 5], attribs[i * 16 + 6])); + ret.write[i].basis.set_column(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; @@ -2304,7 +2148,7 @@ Error GLTFDocument::_serialize_meshes(Ref 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 import_mesh = state->meshes.write[gltf_mesh_i]->get_mesh(); + Ref import_mesh = state->meshes.write[gltf_mesh_i]->get_mesh(); if (import_mesh.is_null()) { continue; } @@ -2319,7 +2163,7 @@ Error GLTFDocument::_serialize_meshes(Ref state) { for (int surface_i = 0; surface_i < import_mesh->get_surface_count(); surface_i++) { Array targets; Dictionary primitive; - Mesh::PrimitiveType primitive_type = import_mesh->surface_get_primitive_type(surface_i); + Mesh::PrimitiveType primitive_type = import_mesh->get_surface_primitive_type(surface_i); switch (primitive_type) { case Mesh::PRIMITIVE_POINTS: { primitive["mode"] = 0; @@ -2354,12 +2198,15 @@ Error GLTFDocument::_serialize_meshes(Ref state) { } } - Array array = import_mesh->surface_get_arrays(surface_i); + Array array = import_mesh->get_surface_arrays(surface_i); + uint32_t format = import_mesh->get_surface_format(surface_i); + int32_t vertex_num = 0; Dictionary attributes; { Vector a = array[Mesh::ARRAY_VERTEX]; ERR_FAIL_COND_V(!a.size(), ERR_INVALID_DATA); attributes["POSITION"] = _encode_accessor_as_vec3(state, a, true); + vertex_num = a.size(); } { Vector a = array[Mesh::ARRAY_TANGENT]; @@ -2402,13 +2249,65 @@ Error GLTFDocument::_serialize_meshes(Ref state) { attributes["TEXCOORD_1"] = _encode_accessor_as_vec2(state, a, true); } } + for (int custom_i = 0; custom_i < 3; custom_i++) { + Vector a = array[Mesh::ARRAY_CUSTOM0 + custom_i]; + if (a.size()) { + int num_channels = 4; + int custom_shift = Mesh::ARRAY_FORMAT_CUSTOM0_SHIFT + custom_i * Mesh::ARRAY_FORMAT_CUSTOM_BITS; + switch ((format >> custom_shift) & Mesh::ARRAY_FORMAT_CUSTOM_MASK) { + case Mesh::ARRAY_CUSTOM_R_FLOAT: + num_channels = 1; + break; + case Mesh::ARRAY_CUSTOM_RG_FLOAT: + num_channels = 2; + break; + case Mesh::ARRAY_CUSTOM_RGB_FLOAT: + num_channels = 3; + break; + case Mesh::ARRAY_CUSTOM_RGBA_FLOAT: + num_channels = 4; + break; + } + int texcoord_i = 2 + 2 * custom_i; + String gltf_texcoord_key; + for (int prev_texcoord_i = 0; prev_texcoord_i < texcoord_i; prev_texcoord_i++) { + gltf_texcoord_key = vformat("TEXCOORD_%d", prev_texcoord_i); + if (!attributes.has(gltf_texcoord_key)) { + Vector empty; + empty.resize(vertex_num); + attributes[gltf_texcoord_key] = _encode_accessor_as_vec2(state, empty, true); + } + } + + LocalVector first_channel; + first_channel.resize(vertex_num); + LocalVector second_channel; + second_channel.resize(vertex_num); + for (int32_t vert_i = 0; vert_i < vertex_num; vert_i++) { + float u = a[vert_i * num_channels + 0]; + float v = (num_channels == 1 ? 0.0f : a[vert_i * num_channels + 1]); + first_channel[vert_i] = Vector2(u, v); + u = 0; + v = 0; + if (num_channels >= 3) { + u = a[vert_i * num_channels + 2]; + v = (num_channels == 3 ? 0.0f : a[vert_i * num_channels + 3]); + second_channel[vert_i] = Vector2(u, v); + } + } + gltf_texcoord_key = vformat("TEXCOORD_%d", texcoord_i); + attributes[gltf_texcoord_key] = _encode_accessor_as_vec2(state, first_channel, true); + gltf_texcoord_key = vformat("TEXCOORD_%d", texcoord_i + 1); + attributes[gltf_texcoord_key] = _encode_accessor_as_vec2(state, second_channel, true); + } + } { Vector a = array[Mesh::ARRAY_COLOR]; if (a.size()) { attributes["COLOR_0"] = _encode_accessor_as_color(state, a, true); } } - Map joint_i_to_bone_i; + HashMap 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) { @@ -2436,8 +2335,29 @@ Error GLTFDocument::_serialize_meshes(Ref state) { } } attributes["JOINTS_0"] = _encode_accessor_as_joints(state, attribs, true); + } else if ((a.size() / (JOINT_GROUP_SIZE * 2)) >= vertex_array.size()) { + Vector joints_0; + joints_0.resize(vertex_num); + Vector joints_1; + joints_1.resize(vertex_num); + int32_t weights_8_count = JOINT_GROUP_SIZE * 2; + for (int32_t vertex_i = 0; vertex_i < vertex_num; 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); } - ERR_FAIL_COND_V((a.size() / (JOINT_GROUP_SIZE * 2)) >= vertex_array.size(), FAILED); } { const Array &a = array[Mesh::ARRAY_WEIGHTS]; @@ -2451,13 +2371,12 @@ Error GLTFDocument::_serialize_meshes(Ref state) { } 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 weights_0; - weights_0.resize(vertex_count); + weights_0.resize(vertex_num); Vector weights_1; - weights_1.resize(vertex_count); + weights_1.resize(vertex_num); int32_t weights_8_count = JOINT_GROUP_SIZE * 2; - for (int32_t vertex_i = 0; vertex_i < vertex_count; vertex_i++) { + for (int32_t vertex_i = 0; vertex_i < vertex_num; 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]; @@ -2491,7 +2410,7 @@ Error GLTFDocument::_serialize_meshes(Ref state) { //generate indices because they need to be swapped for CW/CCW const Vector &vertices = array[Mesh::ARRAY_VERTEX]; Ref st; - st.instance(); + st.instantiate(); st->create_from_triangle_arrays(array); st->index(); Vector generated_indices = st->commit_to_arrays()[Mesh::ARRAY_INDEX]; @@ -2515,12 +2434,11 @@ Error GLTFDocument::_serialize_meshes(Ref state) { print_verbose("glTF: Mesh has targets"); if (import_mesh->get_blend_shape_count()) { ArrayMesh::BlendShapeMode shape_mode = import_mesh->get_blend_shape_mode(); - Array array_morphs = import_mesh->surface_get_blend_shape_arrays(surface_i); - for (int morph_i = 0; morph_i < array_morphs.size(); morph_i++) { - Array array_morph = array_morphs[morph_i]; + 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); Dictionary t; Vector varr = array_morph[Mesh::ARRAY_VERTEX]; - Array mesh_arrays = import_mesh->surface_get_arrays(surface_i); + Array mesh_arrays = import_mesh->get_surface_arrays(surface_i); if (varr.size()) { Vector src_varr = array[Mesh::ARRAY_VERTEX]; if (shape_mode == ArrayMesh::BlendShapeMode::BLEND_SHAPE_MODE_NORMALIZED) { @@ -2553,18 +2471,19 @@ Error GLTFDocument::_serialize_meshes(Ref state) { targets.push_back(t); } } + Variant v; if (surface_i < instance_materials.size()) { v = instance_materials.get(surface_i); } - Ref mat = v; + Ref mat = v; if (!mat.is_valid()) { - mat = import_mesh->surface_get_material(surface_i); + mat = import_mesh->get_surface_material(surface_i); } if (mat.is_valid()) { - Map, 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(); + HashMap, GLTFMaterialIndex>::Iterator material_cache_i = state->material_cache.find(mat); + if (material_cache_i && material_cache_i->value != -1) { + primitive["material"] = material_cache_i->value; } else { GLTFMaterialIndex mat_i = state->materials.size(); state->materials.push_back(mat); @@ -2618,29 +2537,29 @@ Error GLTFDocument::_parse_meshes(Ref state) { return OK; } - bool has_warned = false; 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 mesh; - mesh.instance(); + mesh.instantiate(); 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 import_mesh; - import_mesh.instance(); + Ref import_mesh; + import_mesh.instantiate(); String mesh_name = "mesh"; - if (d.has("name") && !String(d["name"]).empty()) { + if (d.has("name") && !String(d["name"]).is_empty()) { mesh_name = d["name"]; } import_mesh->set_name(_gen_unique_name(state, vformat("%s_%s", state->scene_name, mesh_name))); for (int j = 0; j < primitives.size(); j++) { + uint32_t flags = 0; Dictionary p = primitives[j]; Array array; @@ -2654,16 +2573,18 @@ Error GLTFDocument::_parse_meshes(Ref state) { if (p.has("mode")) { const int mode = p["mode"]; ERR_FAIL_INDEX_V(mode, 7, ERR_FILE_CORRUPT); + // Convert mesh.primitive.mode to Godot Mesh enum. See: + // https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#_mesh_primitive_mode 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 + Mesh::PRIMITIVE_POINTS, // 0 POINTS + Mesh::PRIMITIVE_LINES, // 1 LINES + Mesh::PRIMITIVE_LINES, // 2 LINE_LOOP; loop not supported, should be converted + Mesh::PRIMITIVE_LINE_STRIP, // 3 LINE_STRIP + Mesh::PRIMITIVE_TRIANGLES, // 4 TRIANGLES + Mesh::PRIMITIVE_TRIANGLE_STRIP, // 5 TRIANGLE_STRIP + Mesh::PRIMITIVE_TRIANGLES, // 6 TRIANGLE_FAN 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 +#warning line loop and triangle fan are not supported and need to be converted to lines and triangles #endif }; @@ -2672,8 +2593,11 @@ Error GLTFDocument::_parse_meshes(Ref state) { } ERR_FAIL_COND_V(!a.has("POSITION"), ERR_PARSE_ERROR); + int32_t vertex_num = 0; if (a.has("POSITION")) { - array[Mesh::ARRAY_VERTEX] = _decode_accessor_as_vec3(state, a["POSITION"], true); + PackedVector3Array vertices = _decode_accessor_as_vec3(state, a["POSITION"], true); + array[Mesh::ARRAY_VERTEX] = vertices; + vertex_num = vertices.size(); } if (a.has("NORMAL")) { array[Mesh::ARRAY_NORMAL] = _decode_accessor_as_vec3(state, a["NORMAL"], true); @@ -2687,20 +2611,86 @@ Error GLTFDocument::_parse_meshes(Ref state) { if (a.has("TEXCOORD_1")) { array[Mesh::ARRAY_TEX_UV2] = _decode_accessor_as_vec2(state, a["TEXCOORD_1"], true); } + for (int custom_i = 0; custom_i < 3; custom_i++) { + Vector cur_custom; + Vector texcoord_first; + Vector texcoord_second; + + int texcoord_i = 2 + 2 * custom_i; + String gltf_texcoord_key = vformat("TEXCOORD_%d", texcoord_i); + int num_channels = 0; + if (a.has(gltf_texcoord_key)) { + texcoord_first = _decode_accessor_as_vec2(state, a[gltf_texcoord_key], true); + num_channels = 2; + } + gltf_texcoord_key = vformat("TEXCOORD_%d", texcoord_i + 1); + if (a.has(gltf_texcoord_key)) { + texcoord_second = _decode_accessor_as_vec2(state, a[gltf_texcoord_key], true); + num_channels = 4; + } + if (!num_channels) { + break; + } + if (num_channels == 2 || num_channels == 4) { + cur_custom.resize(vertex_num * num_channels); + for (int32_t uv_i = 0; uv_i < texcoord_first.size() && uv_i < vertex_num; uv_i++) { + cur_custom.write[uv_i * num_channels + 0] = texcoord_first[uv_i].x; + cur_custom.write[uv_i * num_channels + 1] = texcoord_first[uv_i].y; + } + // Vector.resize seems to not zero-initialize. Ensure all unused elements are 0: + for (int32_t uv_i = texcoord_first.size(); uv_i < vertex_num; uv_i++) { + cur_custom.write[uv_i * num_channels + 0] = 0; + cur_custom.write[uv_i * num_channels + 1] = 0; + } + } + if (num_channels == 4) { + for (int32_t uv_i = 0; uv_i < texcoord_second.size() && uv_i < vertex_num; uv_i++) { + // num_channels must be 4 + cur_custom.write[uv_i * num_channels + 2] = texcoord_second[uv_i].x; + cur_custom.write[uv_i * num_channels + 3] = texcoord_second[uv_i].y; + } + // Vector.resize seems to not zero-initialize. Ensure all unused elements are 0: + for (int32_t uv_i = texcoord_second.size(); uv_i < vertex_num; uv_i++) { + cur_custom.write[uv_i * num_channels + 2] = 0; + cur_custom.write[uv_i * num_channels + 3] = 0; + } + } + if (cur_custom.size() > 0) { + array[Mesh::ARRAY_CUSTOM0 + custom_i] = cur_custom; + int custom_shift = Mesh::ARRAY_FORMAT_CUSTOM0_SHIFT + custom_i * Mesh::ARRAY_FORMAT_CUSTOM_BITS; + if (num_channels == 2) { + flags |= Mesh::ARRAY_CUSTOM_RG_FLOAT << custom_shift; + } else { + flags |= Mesh::ARRAY_CUSTOM_RGBA_FLOAT << custom_shift; + } + } + } 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")) { + if (a.has("JOINTS_0") && !a.has("JOINTS_1")) { array[Mesh::ARRAY_BONES] = _decode_accessor_as_ints(state, a["JOINTS_0"], true); - } - if (a.has("WEIGHTS_1") && a.has("JOINTS_1")) { - if (!has_warned) { - WARN_PRINT("glTF: Meshes use more than 4 bone joints"); - has_warned = 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; + Vector joints; + joints.resize(vertex_num * weight_8_count); + for (int32_t vertex_i = 0; vertex_i < vertex_num; 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")) { + if (a.has("WEIGHTS_0") && !a.has("WEIGHTS_1")) { Vector 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(); @@ -2721,6 +2711,50 @@ Error GLTFDocument::_parse_meshes(Ref state) { } } array[Mesh::ARRAY_WEIGHTS] = weights; + } else if (a.has("WEIGHTS_0") && a.has("WEIGHTS_1")) { + Vector weights_0 = _decode_accessor_as_floats(state, a["WEIGHTS_0"], true); + Vector weights_1 = _decode_accessor_as_floats(state, a["WEIGHTS_1"], true); + Vector weights; + ERR_FAIL_COND_V(weights_0.size() != weights_1.size(), ERR_INVALID_DATA); + int32_t weight_8_count = JOINT_GROUP_SIZE * 2; + weights.resize(vertex_num * weight_8_count); + for (int32_t vertex_i = 0; vertex_i < vertex_num; 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")) { @@ -2757,14 +2791,18 @@ Error GLTFDocument::_parse_meshes(Ref state) { bool generate_tangents = (primitive == Mesh::PRIMITIVE_TRIANGLES && !a.has("TANGENT") && a.has("TEXCOORD_0") && a.has("NORMAL")); + Ref mesh_surface_tool; + mesh_surface_tool.instantiate(); + mesh_surface_tool->create_from_triangle_arrays(array); + if (a.has("JOINTS_0") && a.has("JOINTS_1")) { + mesh_surface_tool->set_skin_weight_count(SurfaceTool::SKIN_8_WEIGHTS); + } + mesh_surface_tool->index(); if (generate_tangents) { //must generate mikktspace tangents.. ergh.. - Ref st; - st.instance(); - st->create_from_triangle_arrays(array); - st->generate_tangents(); - array = st->commit_to_arrays(); + mesh_surface_tool->generate_tangents(); } + array = mesh_surface_tool->commit_to_arrays(); Array morphs; //blend shapes @@ -2794,8 +2832,6 @@ Error GLTFDocument::_parse_meshes(Ref state) { array_copy[l] = array[l]; } - array_copy[Mesh::ARRAY_INDEX] = Variant(); - if (t.has("POSITION")) { Vector varr = _decode_accessor_as_vec3(state, t["POSITION"], true); const Vector src_varr = array[Mesh::ARRAY_VERTEX]; @@ -2874,40 +2910,55 @@ Error GLTFDocument::_parse_meshes(Ref state) { array_copy[Mesh::ARRAY_TANGENT] = tangents_v4; } + Ref blend_surface_tool; + blend_surface_tool.instantiate(); + blend_surface_tool->create_from_triangle_arrays(array_copy); + if (a.has("JOINTS_0") && a.has("JOINTS_1")) { + blend_surface_tool->set_skin_weight_count(SurfaceTool::SKIN_8_WEIGHTS); + } + blend_surface_tool->index(); if (generate_tangents) { - Ref st; - st.instance(); - st->create_from_triangle_arrays(array_copy); - st->deindex(); - st->generate_tangents(); - array_copy = st->commit_to_arrays(); + blend_surface_tool->generate_tangents(); + } + array_copy = blend_surface_tool->commit_to_arrays(); + + // Enforce blend shape mask array format + for (int l = 0; l < Mesh::ARRAY_MAX; l++) { + if (!(Mesh::ARRAY_FORMAT_BLEND_SHAPE_MASK & (1 << l))) { + array_copy[l] = Variant(); + } } morphs.push_back(array_copy); } } - //just add it + Ref mat; + String mat_name; + if (!state->discard_meshes_and_materials) { + if (p.has("material")) { + const int material = p["material"]; + ERR_FAIL_INDEX_V(material, state->materials.size(), ERR_FILE_CORRUPT); + Ref mat3d = state->materials[material]; + ERR_FAIL_NULL_V(mat3d, ERR_FILE_CORRUPT); + if (has_vertex_color) { + mat3d->set_flag(BaseMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + } + mat = mat3d; - Ref mat; - if (p.has("material")) { - const int material = p["material"]; - ERR_FAIL_INDEX_V(material, state->materials.size(), ERR_FILE_CORRUPT); - Ref mat3d = state->materials[material]; - if (has_vertex_color) { - mat3d->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + } else { + Ref mat3d; + mat3d.instantiate(); + if (has_vertex_color) { + mat3d->set_flag(BaseMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + } + mat = mat3d; } - mat = mat3d; - - } else if (has_vertex_color) { - Ref mat3d; - mat3d.instance(); - mat3d->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); - mat = mat3d; + ERR_FAIL_NULL_V(mat, ERR_FILE_CORRUPT); + mat_name = mat->get_name(); } - int32_t mat_idx = import_mesh->get_surface_count(); - import_mesh->add_surface_from_arrays(primitive, array, morphs, state->compress_flags); - import_mesh->surface_set_material(mat_idx, mat); + import_mesh->add_surface(primitive, array, morphs, + Dictionary(), mat, mat_name, flags); } Vector blend_weights; @@ -2943,31 +2994,31 @@ Error GLTFDocument::_serialize_images(Ref state, const String &p_path ERR_CONTINUE(state->images[i].is_null()); - Ref image = state->images[i]->get_data(); + Ref image = state->images[i]->get_image(); ERR_CONTINUE(image.is_null()); - if (p_path.to_lower().ends_with("glb")) { + if (p_path.to_lower().ends_with("glb") || p_path.is_empty()) { GLTFBufferViewIndex bvi; Ref bv; - bv.instance(); + bv.instantiate(); 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); - PoolVector buffer; + Vector buffer; Ref img_tex = image; if (img_tex.is_valid()) { - image = img_tex->get_data(); + image = img_tex->get_image(); } 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); - memcpy(&state->buffers.write[bi].write[bv->byte_offset], buffer.read().ptr(), buffer.size()); + memcpy(&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); @@ -2975,23 +3026,22 @@ Error GLTFDocument::_serialize_images(Ref state, const String &p_path d["bufferView"] = bvi; d["mimeType"] = "image/png"; } else { + ERR_FAIL_COND_V(p_path.is_empty(), ERR_INVALID_PARAMETER); String name = state->images[i]->get_name(); - if (name.empty()) { + if (name.is_empty()) { name = itos(i); } name = _gen_unique_name(state, name); - name = name.pad_zeros(3); - Ref<_Directory> dir; - dir.instance(); + name = name.pad_zeros(3) + ".png"; 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); + String path = p_path.get_base_dir(); + String new_texture_dir = path + "/" + texture_dir; + Ref da = DirAccess::open(path); + if (!da->dir_exists(new_texture_dir)) { + da->make_dir(new_texture_dir); } - name = name + ".png"; image->save_png(new_texture_dir.plus_file(name)); - d["uri"] = texture_dir.plus_file(name); + d["uri"] = texture_dir.plus_file(name).uri_encode(); } images.push_back(d); } @@ -3007,6 +3057,7 @@ Error GLTFDocument::_serialize_images(Ref state, const String &p_path } Error GLTFDocument::_parse_images(Ref state, const String &p_base_path) { + ERR_FAIL_NULL_V(state, ERR_INVALID_PARAMETER); if (!state->json.has("images")) { return OK; } @@ -3022,7 +3073,7 @@ Error GLTFDocument::_parse_images(Ref state, const String &p_base_pat // - 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 Pandemonium parse the data to figure out if it's PNG or JPEG. + // 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. @@ -3051,14 +3102,14 @@ Error GLTFDocument::_parse_images(Ref state, const String &p_base_pat !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()); // Placeholder to keep count. + state->images.push_back(Ref()); // 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.empty()) { + if (mimetype.is_empty()) { if (uri.begins_with("data:image/png;base64")) { mimetype = "image/png"; } else if (uri.begins_with("data:image/jpeg;base64")) { @@ -3066,14 +3117,15 @@ Error GLTFDocument::_parse_images(Ref state, const String &p_base_pat } } } else { // Relative path to an external image file. - uri = uri.http_unescape(); + ERR_FAIL_COND_V(p_base_path.is_empty(), ERR_INVALID_PARAMETER); + uri = uri.uri_decode(); 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 Pandemonium, so we'd have to load as a buffer (i.e. embedded in + // 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 texture = ResourceLoader::load(uri); + Ref texture = ResourceLoader::load(uri); if (texture.is_valid()) { state->images.push_back(texture); continue; @@ -3084,20 +3136,20 @@ Error GLTFDocument::_parse_images(Ref state, const String &p_base_pat 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()); // Placeholder to keep count. + state->images.push_back(Ref()); // 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()); // Placeholder to keep count. + state->images.push_back(Ref()); // 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.empty(), ERR_FILE_CORRUPT, + 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"]; @@ -3144,15 +3196,10 @@ Error GLTFDocument::_parse_images(Ref state, const String &p_base_pat // Now we've done our best, fix your scenes. if (img.is_null()) { ERR_PRINT(vformat("glTF: Couldn't load image index '%d' with its given mimetype: %s.", i, mimetype)); - state->images.push_back(Ref()); + state->images.push_back(Ref()); continue; } - - Ref t; - t.instance(); - t->create_from_image(img); - - state->images.push_back(t); + state->images.push_back(ImageTexture::create_from_image(img)); } print_verbose("glTF: Total images: " + itos(state->images.size())); @@ -3190,7 +3237,7 @@ Error GLTFDocument::_parse_textures(Ref state) { ERR_FAIL_COND_V(!d.has("source"), ERR_PARSE_ERROR); Ref t; - t.instance(); + t.instantiate(); t->set_src_image(d["source"]); state->textures.push_back(t); } @@ -3198,11 +3245,11 @@ Error GLTFDocument::_parse_textures(Ref state) { return OK; } -GLTFTextureIndex GLTFDocument::_set_texture(Ref state, Ref p_texture) { +GLTFTextureIndex GLTFDocument::_set_texture(Ref state, Ref p_texture) { ERR_FAIL_COND_V(p_texture.is_null(), -1); Ref gltf_texture; - gltf_texture.instance(); - ERR_FAIL_COND_V(p_texture->get_data().is_null(), -1); + gltf_texture.instantiate(); + ERR_FAIL_COND_V(p_texture->get_image().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); @@ -3211,11 +3258,11 @@ GLTFTextureIndex GLTFDocument::_set_texture(Ref state, Ref p return gltf_texture_i; } -Ref GLTFDocument::_get_texture(Ref state, const GLTFTextureIndex p_texture) { - ERR_FAIL_INDEX_V(p_texture, state->textures.size(), Ref()); +Ref GLTFDocument::_get_texture(Ref state, const GLTFTextureIndex p_texture) { + ERR_FAIL_INDEX_V(p_texture, state->textures.size(), Ref()); const GLTFImageIndex image = state->textures[p_texture]->get_src_image(); - ERR_FAIL_INDEX_V(image, state->images.size(), Ref()); + ERR_FAIL_INDEX_V(image, state->images.size(), Ref()); return state->images[image]; } @@ -3225,19 +3272,19 @@ Error GLTFDocument::_serialize_materials(Ref state) { for (int32_t i = 0; i < state->materials.size(); i++) { Dictionary d; - Ref material = state->materials[i]; + Ref material = state->materials[i]; if (material.is_null()) { materials.push_back(d); continue; } - if (!material->get_name().empty()) { + 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(); + const Color c = material->get_albedo().srgb_to_linear(); arr.push_back(c.r); arr.push_back(c.g); arr.push_back(c.b); @@ -3246,47 +3293,51 @@ Error GLTFDocument::_serialize_materials(Ref state) { } { Dictionary bct; - Ref albedo_texture = material->get_texture(SpatialMaterial::TEXTURE_ALBEDO); + Ref albedo_texture = material->get_texture(BaseMaterial3D::TEXTURE_ALBEDO); GLTFTextureIndex gltf_texture_index = -1; - if (albedo_texture.is_valid() && albedo_texture->get_data().is_valid()) { + if (albedo_texture.is_valid() && albedo_texture->get_image().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); + Dictionary extensions = _serialize_texture_transform_uv1(material); + if (!extensions.is_empty()) { + bct["extensions"] = extensions; + state->use_khr_texture_transform = true; + } mr["baseColorTexture"] = bct; } } mr["metallicFactor"] = material->get_metallic(); mr["roughnessFactor"] = material->get_roughness(); - bool has_roughness = material->get_texture(SpatialMaterial::TEXTURE_ROUGHNESS).is_valid() && material->get_texture(SpatialMaterial::TEXTURE_ROUGHNESS)->get_data().is_valid(); - bool has_ao = material->get_feature(SpatialMaterial::FEATURE_AMBIENT_OCCLUSION) && material->get_texture(SpatialMaterial::TEXTURE_AMBIENT_OCCLUSION).is_valid(); - bool has_metalness = material->get_texture(SpatialMaterial::TEXTURE_METALLIC).is_valid() && material->get_texture(SpatialMaterial::TEXTURE_METALLIC)->get_data().is_valid(); + bool has_roughness = material->get_texture(BaseMaterial3D::TEXTURE_ROUGHNESS).is_valid() && material->get_texture(BaseMaterial3D::TEXTURE_ROUGHNESS)->get_image().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_image().is_valid(); if (has_ao || has_roughness || has_metalness) { Dictionary mrt; - Ref roughness_texture = material->get_texture(SpatialMaterial::TEXTURE_ROUGHNESS); - SpatialMaterial::TextureChannel roughness_channel = material->get_roughness_texture_channel(); - Ref metallic_texture = material->get_texture(SpatialMaterial::TEXTURE_METALLIC); - SpatialMaterial::TextureChannel metalness_channel = material->get_metallic_texture_channel(); - Ref ao_texture = material->get_texture(SpatialMaterial::TEXTURE_AMBIENT_OCCLUSION); - SpatialMaterial::TextureChannel ao_channel = material->get_ao_texture_channel(); + Ref roughness_texture = material->get_texture(BaseMaterial3D::TEXTURE_ROUGHNESS); + BaseMaterial3D::TextureChannel roughness_channel = material->get_roughness_texture_channel(); + Ref metallic_texture = material->get_texture(BaseMaterial3D::TEXTURE_METALLIC); + BaseMaterial3D::TextureChannel metalness_channel = material->get_metallic_texture_channel(); + Ref ao_texture = material->get_texture(BaseMaterial3D::TEXTURE_AMBIENT_OCCLUSION); + BaseMaterial3D::TextureChannel ao_channel = material->get_ao_texture_channel(); Ref orm_texture; - orm_texture.instance(); + orm_texture.instantiate(); Ref orm_image; - orm_image.instance(); + orm_image.instantiate(); int32_t height = 0; int32_t width = 0; Ref ao_image; if (has_ao) { height = ao_texture->get_height(); width = ao_texture->get_width(); - ao_image = ao_texture->get_data(); + ao_image = ao_texture->get_image(); Ref img_tex = ao_image; if (img_tex.is_valid()) { - ao_image = img_tex->get_data(); + ao_image = img_tex->get_image(); } if (ao_image->is_compressed()) { ao_image->decompress(); @@ -3296,10 +3347,10 @@ Error GLTFDocument::_serialize_materials(Ref state) { if (has_roughness) { height = roughness_texture->get_height(); width = roughness_texture->get_width(); - roughness_image = roughness_texture->get_data(); + roughness_image = roughness_texture->get_image(); Ref img_tex = roughness_image; if (img_tex.is_valid()) { - roughness_image = img_tex->get_data(); + roughness_image = img_tex->get_image(); } if (roughness_image->is_compressed()) { roughness_image->decompress(); @@ -3309,17 +3360,17 @@ Error GLTFDocument::_serialize_materials(Ref state) { if (has_metalness) { height = metallic_texture->get_height(); width = metallic_texture->get_width(); - metallness_image = metallic_texture->get_data(); + metallness_image = metallic_texture->get_image(); Ref img_tex = metallness_image; if (img_tex.is_valid()) { - metallness_image = img_tex->get_data(); + metallness_image = img_tex->get_image(); } if (metallness_image->is_compressed()) { metallness_image->decompress(); } } - Ref albedo_texture = material->get_texture(SpatialMaterial::TEXTURE_ALBEDO); - if (albedo_texture.is_valid() && albedo_texture->get_data().is_valid()) { + Ref albedo_texture = material->get_texture(BaseMaterial3D::TEXTURE_ALBEDO); + if (albedo_texture.is_valid() && albedo_texture->get_image().is_valid()) { height = albedo_texture->get_height(); width = albedo_texture->get_width(); } @@ -3333,91 +3384,87 @@ Error GLTFDocument::_serialize_materials(Ref state) { if (metallness_image.is_valid() && metallness_image->get_size() != Vector2(width, height)) { metallness_image->resize(width, height, Image::INTERPOLATE_LANCZOS); } - orm_image->lock(); 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) { - ao_image->lock(); - if (SpatialMaterial::TextureChannel::TEXTURE_CHANNEL_RED == ao_channel) { + if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_RED == ao_channel) { c.r = ao_image->get_pixel(w, h).r; - } else if (SpatialMaterial::TextureChannel::TEXTURE_CHANNEL_GREEN == ao_channel) { + } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_GREEN == ao_channel) { c.r = ao_image->get_pixel(w, h).g; - } else if (SpatialMaterial::TextureChannel::TEXTURE_CHANNEL_BLUE == ao_channel) { + } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_BLUE == ao_channel) { c.r = ao_image->get_pixel(w, h).b; - } else if (SpatialMaterial::TextureChannel::TEXTURE_CHANNEL_ALPHA == ao_channel) { + } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_ALPHA == ao_channel) { c.r = ao_image->get_pixel(w, h).a; } - ao_image->lock(); } if (has_roughness) { - roughness_image->lock(); - if (SpatialMaterial::TextureChannel::TEXTURE_CHANNEL_RED == roughness_channel) { + if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_RED == roughness_channel) { c.g = roughness_image->get_pixel(w, h).r; - } else if (SpatialMaterial::TextureChannel::TEXTURE_CHANNEL_GREEN == roughness_channel) { + } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_GREEN == roughness_channel) { c.g = roughness_image->get_pixel(w, h).g; - } else if (SpatialMaterial::TextureChannel::TEXTURE_CHANNEL_BLUE == roughness_channel) { + } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_BLUE == roughness_channel) { c.g = roughness_image->get_pixel(w, h).b; - } else if (SpatialMaterial::TextureChannel::TEXTURE_CHANNEL_ALPHA == roughness_channel) { + } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_ALPHA == roughness_channel) { c.g = roughness_image->get_pixel(w, h).a; } - roughness_image->unlock(); } if (has_metalness) { - metallness_image->lock(); - if (SpatialMaterial::TextureChannel::TEXTURE_CHANNEL_RED == metalness_channel) { + if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_RED == metalness_channel) { c.b = metallness_image->get_pixel(w, h).r; - } else if (SpatialMaterial::TextureChannel::TEXTURE_CHANNEL_GREEN == metalness_channel) { + } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_GREEN == metalness_channel) { c.b = metallness_image->get_pixel(w, h).g; - } else if (SpatialMaterial::TextureChannel::TEXTURE_CHANNEL_BLUE == metalness_channel) { + } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_BLUE == metalness_channel) { c.b = metallness_image->get_pixel(w, h).b; - } else if (SpatialMaterial::TextureChannel::TEXTURE_CHANNEL_ALPHA == metalness_channel) { + } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_ALPHA == metalness_channel) { c.b = metallness_image->get_pixel(w, h).a; } - metallness_image->unlock(); } orm_image->set_pixel(w, h, c); } } - orm_image->unlock(); orm_image->generate_mipmaps(); - orm_texture->create_from_image(orm_image); + orm_texture->set_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; + Dictionary occt; + occt["index"] = orm_texture_index; + d["occlusionTexture"] = occt; } if (has_roughness || has_metalness) { mrt["index"] = orm_texture_index; - mrt["extensions"] = _serialize_texture_transform_uv1(material); + Dictionary extensions = _serialize_texture_transform_uv1(material); + if (!extensions.is_empty()) { + mrt["extensions"] = extensions; + state->use_khr_texture_transform = true; + } mr["metallicRoughnessTexture"] = mrt; } } d["pbrMetallicRoughness"] = mr; } - if (material->get_feature(SpatialMaterial::FEATURE_NORMAL_MAPPING)) { + if (material->get_feature(BaseMaterial3D::FEATURE_NORMAL_MAPPING)) { Dictionary nt; Ref tex; - tex.instance(); + tex.instantiate(); { - Ref normal_texture = material->get_texture(SpatialMaterial::TEXTURE_NORMAL); + Ref normal_texture = material->get_texture(BaseMaterial3D::TEXTURE_NORMAL); if (normal_texture.is_valid()) { // Code for uncompressing RG normal maps - Ref img = normal_texture->get_data(); + Ref img = normal_texture->get_image(); if (img.is_valid()) { Ref img_tex = img; if (img_tex.is_valid()) { - img = img_tex->get_data(); + img = img_tex->get_image(); } img->decompress(); img->convert(Image::FORMAT_RGBA8); - img->lock(); + img->convert_ra_rgba8_to_rg(); 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); @@ -3429,13 +3476,12 @@ Error GLTFDocument::_serialize_materials(Ref state) { img->set_pixel(x, y, c); } } - img->unlock(); - tex->create_from_image(img); + tex->set_image(img); } } } GLTFTextureIndex gltf_texture_index = -1; - if (tex.is_valid() && tex->get_data().is_valid()) { + if (tex.is_valid() && tex->get_image().is_valid()) { tex->set_name(material->get_name() + "_normal"); gltf_texture_index = _set_texture(state, tex); } @@ -3446,19 +3492,19 @@ Error GLTFDocument::_serialize_materials(Ref state) { } } - if (material->get_feature(SpatialMaterial::FEATURE_EMISSION)) { - const Color c = material->get_emission().to_srgb(); + if (material->get_feature(BaseMaterial3D::FEATURE_EMISSION)) { + const Color c = material->get_emission().linear_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(SpatialMaterial::FEATURE_EMISSION)) { + if (material->get_feature(BaseMaterial3D::FEATURE_EMISSION)) { Dictionary et; - Ref emission_texture = material->get_texture(SpatialMaterial::TEXTURE_EMISSION); + Ref emission_texture = material->get_texture(BaseMaterial3D::TEXTURE_EMISSION); GLTFTextureIndex gltf_texture_index = -1; - if (emission_texture.is_valid() && emission_texture->get_data().is_valid()) { + if (emission_texture.is_valid() && emission_texture->get_image().is_valid()) { emission_texture->set_name(material->get_name() + "_emission"); gltf_texture_index = _set_texture(state, emission_texture); } @@ -3468,17 +3514,15 @@ Error GLTFDocument::_serialize_materials(Ref state) { d["emissiveTexture"] = et; } } - const bool ds = material->get_cull_mode() == SpatialMaterial::CULL_DISABLED; + const bool ds = material->get_cull_mode() == BaseMaterial3D::CULL_DISABLED; if (ds) { d["doubleSided"] = ds; } - if (material->get_feature(SpatialMaterial::FEATURE_TRANSPARENT)) { - if (material->get_flag(SpatialMaterial::FLAG_USE_ALPHA_SCISSOR)) { - d["alphaMode"] = "MASK"; - d["alphaCutoff"] = material->get_alpha_scissor_threshold(); - } else { - d["alphaMode"] = "BLEND"; - } + 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); } @@ -3500,15 +3544,14 @@ Error GLTFDocument::_parse_materials(Ref state) { for (GLTFMaterialIndex i = 0; i < materials.size(); i++) { const Dictionary &d = materials[i]; - Ref material; - material.instance(); - if (d.has("name") && !String(d["name"]).empty()) { + Ref material; + material.instantiate(); + if (d.has("name") && !String(d["name"]).is_empty()) { material->set_name(d["name"]); } else { material->set_name(vformat("material_%s", itos(i))); } - - material->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + 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"]; @@ -3518,21 +3561,21 @@ Error GLTFDocument::_parse_materials(Ref state) { Dictionary sgm = pbr_spec_gloss_extensions["KHR_materials_pbrSpecularGlossiness"]; Ref spec_gloss; - spec_gloss.instance(); + spec_gloss.instantiate(); if (sgm.has("diffuseTexture")) { const Dictionary &diffuse_texture_dict = sgm["diffuseTexture"]; if (diffuse_texture_dict.has("index")) { - Ref diffuse_texture = _get_texture(state, diffuse_texture_dict["index"]); + Ref 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(SpatialMaterial::TEXTURE_ALBEDO, diffuse_texture); + spec_gloss->diffuse_img = diffuse_texture->get_image(); + 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(); + const Color c = Color(arr[0], arr[1], arr[2], arr[3]).linear_to_srgb(); spec_gloss->diffuse_factor = c; material->set_albedo(spec_gloss->diffuse_factor); } @@ -3550,9 +3593,9 @@ Error GLTFDocument::_parse_materials(Ref state) { if (sgm.has("specularGlossinessTexture")) { const Dictionary &spec_gloss_texture = sgm["specularGlossinessTexture"]; if (spec_gloss_texture.has("index")) { - const Ref orig_texture = _get_texture(state, spec_gloss_texture["index"]); + const Ref 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->spec_gloss_img = orig_texture->get_image(); } } } @@ -3563,14 +3606,14 @@ Error GLTFDocument::_parse_materials(Ref state) { 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(); + const Color c = Color(arr[0], arr[1], arr[2], arr[3]).linear_to_srgb(); material->set_albedo(c); } if (mr.has("baseColorTexture")) { const Dictionary &bct = mr["baseColorTexture"]; if (bct.has("index")) { - material->set_texture(SpatialMaterial::TEXTURE_ALBEDO, _get_texture(state, bct["index"])); + material->set_texture(BaseMaterial3D::TEXTURE_ALBEDO, _get_texture(state, bct["index"])); } if (!mr.has("baseColorFactor")) { material->set_albedo(Color(1, 1, 1)); @@ -3593,11 +3636,11 @@ Error GLTFDocument::_parse_materials(Ref state) { if (mr.has("metallicRoughnessTexture")) { const Dictionary &bct = mr["metallicRoughnessTexture"]; if (bct.has("index")) { - const Ref t = _get_texture(state, bct["index"]); - material->set_texture(SpatialMaterial::TEXTURE_METALLIC, t); - material->set_metallic_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_BLUE); - material->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, t); - material->set_roughness_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GREEN); + const Ref 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); } @@ -3611,8 +3654,8 @@ Error GLTFDocument::_parse_materials(Ref state) { if (d.has("normalTexture")) { const Dictionary &bct = d["normalTexture"]; if (bct.has("index")) { - material->set_texture(SpatialMaterial::TEXTURE_NORMAL, _get_texture(state, bct["index"])); - material->set_feature(SpatialMaterial::FEATURE_NORMAL_MAPPING, true); + 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"]); @@ -3621,17 +3664,17 @@ Error GLTFDocument::_parse_materials(Ref state) { if (d.has("occlusionTexture")) { const Dictionary &bct = d["occlusionTexture"]; if (bct.has("index")) { - material->set_texture(SpatialMaterial::TEXTURE_AMBIENT_OCCLUSION, _get_texture(state, bct["index"])); - material->set_ao_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_RED); - material->set_feature(SpatialMaterial::FEATURE_AMBIENT_OCCLUSION, true); + 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(SpatialMaterial::FEATURE_EMISSION, true); + const Color c = Color(arr[0], arr[1], arr[2]).linear_to_srgb(); + material->set_feature(BaseMaterial3D::FEATURE_EMISSION, true); material->set_emission(c); } @@ -3639,8 +3682,8 @@ Error GLTFDocument::_parse_materials(Ref state) { if (d.has("emissiveTexture")) { const Dictionary &bct = d["emissiveTexture"]; if (bct.has("index")) { - material->set_texture(SpatialMaterial::TEXTURE_EMISSION, _get_texture(state, bct["index"])); - material->set_feature(SpatialMaterial::FEATURE_EMISSION, true); + 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)); } } @@ -3648,17 +3691,15 @@ Error GLTFDocument::_parse_materials(Ref state) { if (d.has("doubleSided")) { const bool ds = d["doubleSided"]; if (ds) { - material->set_cull_mode(SpatialMaterial::CULL_DISABLED); + material->set_cull_mode(BaseMaterial3D::CULL_DISABLED); } } - if (d.has("alphaMode")) { const String &am = d["alphaMode"]; if (am == "BLEND") { - material->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); - material->set_depth_draw_mode(SpatialMaterial::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); + material->set_transparency(BaseMaterial3D::TRANSPARENCY_ALPHA_DEPTH_PRE_PASS); } else if (am == "MASK") { - material->set_flag(SpatialMaterial::FLAG_USE_ALPHA_SCISSOR, true); + material->set_transparency(BaseMaterial3D::TRANSPARENCY_ALPHA_SCISSOR); if (d.has("alphaCutoff")) { material->set_alpha_scissor_threshold(d["alphaCutoff"]); } else { @@ -3674,7 +3715,7 @@ Error GLTFDocument::_parse_materials(Ref state) { return OK; } -void GLTFDocument::_set_texture_transform_uv1(const Dictionary &d, Ref material) { +void GLTFDocument::_set_texture_transform_uv1(const Dictionary &d, Ref material) { if (d.has("extensions")) { const Dictionary &extensions = d["extensions"]; if (extensions.has("KHR_texture_transform")) { @@ -3694,7 +3735,7 @@ void GLTFDocument::_set_texture_transform_uv1(const Dictionary &d, Ref r_spec_gloss, Ref p_material) { +void GLTFDocument::spec_gloss_to_rough_metal(Ref r_spec_gloss, Ref p_material) { if (r_spec_gloss->spec_gloss_img.is_null()) { return; } @@ -3702,13 +3743,12 @@ void GLTFDocument::spec_gloss_to_rough_metal(Ref r_spec_gloss, Re return; } Ref rm_img; - rm_img.instance(); + rm_img.instantiate(); 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); - rm_img->lock(); r_spec_gloss->spec_gloss_img->decompress(); if (r_spec_gloss->diffuse_img.is_valid()) { r_spec_gloss->diffuse_img->decompress(); @@ -3717,12 +3757,11 @@ void GLTFDocument::spec_gloss_to_rough_metal(Ref r_spec_gloss, Re } 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(); + const Color specular_pixel = r_spec_gloss->spec_gloss_img->get_pixel(x, y).srgb_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); - r_spec_gloss->diffuse_img->lock(); - diffuse *= r_spec_gloss->diffuse_img->get_pixel(x, y).to_linear(); + diffuse *= r_spec_gloss->diffuse_img->get_pixel(x, y).srgb_to_linear(); float metallic = 0.0f; Color base_color; spec_gloss_to_metal_base_color(specular, diffuse, base_color, metallic); @@ -3732,34 +3771,29 @@ void GLTFDocument::spec_gloss_to_rough_metal(Ref r_spec_gloss, Re if (!Math::is_equal_approx(mr.g, 1.0f)) { has_roughness = true; } - if (!Math::is_equal_approx(mr.b, 0.0f)) { + if (!Math::is_zero_approx(mr.b)) { has_metal = true; } mr.g *= r_spec_gloss->gloss_factor; mr.g = 1.0f - mr.g; rm_img->set_pixel(x, y, mr); - r_spec_gloss->diffuse_img->set_pixel(x, y, base_color.to_srgb()); - r_spec_gloss->diffuse_img->unlock(); + if (r_spec_gloss->diffuse_img.is_valid()) { + r_spec_gloss->diffuse_img->set_pixel(x, y, base_color.linear_to_srgb()); + } } } - rm_img->unlock(); rm_img->generate_mipmaps(); r_spec_gloss->diffuse_img->generate_mipmaps(); - Ref diffuse_image_texture; - diffuse_image_texture.instance(); - diffuse_image_texture->create_from_image(r_spec_gloss->diffuse_img); - p_material->set_texture(SpatialMaterial::TEXTURE_ALBEDO, diffuse_image_texture); - Ref rm_image_texture; - rm_image_texture.instance(); - rm_image_texture->create_from_image(rm_img); + p_material->set_texture(BaseMaterial3D::TEXTURE_ALBEDO, ImageTexture::create_from_image(r_spec_gloss->diffuse_img)); + Ref rm_image_texture = ImageTexture::create_from_image(rm_img); if (has_roughness) { - p_material->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, rm_image_texture); - p_material->set_roughness_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GREEN); + 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(SpatialMaterial::TEXTURE_METALLIC, rm_image_texture); - p_material->set_metallic_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_BLUE); + p_material->set_texture(BaseMaterial3D::TEXTURE_METALLIC, rm_image_texture); + p_material->set_metallic_texture_channel(BaseMaterial3D::TEXTURE_CHANNEL_BLUE); } } @@ -3778,10 +3812,7 @@ void GLTFDocument::spec_gloss_to_metal_base_color(const Color &p_specular_factor 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); + r_base_color = r_base_color.clamp(); } GLTFNodeIndex GLTFDocument::_find_highest_node(Ref state, const Vector &subset) { @@ -3856,7 +3887,7 @@ void GLTFDocument::_capture_nodes_for_multirooted_skin(Ref state, Ref } // 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 Pandemonium) would have this same issue. + // 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) { @@ -4026,7 +4057,7 @@ Error GLTFDocument::_parse_skins(Ref state) { const Dictionary &d = skins[i]; Ref skin; - skin.instance(); + skin.instantiate(); ERR_FAIL_COND_V(!d.has("joints"), ERR_PARSE_ERROR); @@ -4047,7 +4078,7 @@ Error GLTFDocument::_parse_skins(Ref state) { state->nodes.write[node]->joint = true; } - if (d.has("name") && !String(d["name"]).empty()) { + if (d.has("name") && !String(d["name"]).is_empty()) { skin->set_name(d["name"]); } else { skin->set_name(vformat("skin_%s", itos(i))); @@ -4154,7 +4185,7 @@ Error GLTFDocument::_determine_skeletons(Ref state) { for (GLTFSkeletonIndex skel_i = 0; skel_i < skeleton_owners.size(); ++skel_i) { const GLTFNodeIndex skeleton_owner = skeleton_owners[skel_i]; Ref skeleton; - skeleton.instance(); + skeleton.instantiate(); Vector skeleton_nodes; skeleton_sets.get_members(skeleton_nodes, skeleton_owner); @@ -4283,13 +4314,8 @@ Error GLTFDocument::_determine_skeleton_roots(Ref state, const GLTFSk } roots.sort(); - PoolVector roots_array; - roots_array.resize(roots.size()); - PoolVector::Write write_roots = roots_array.write(); - for (int32_t root_i = 0; root_i < roots_array.size(); root_i++) { - write_roots[root_i] = roots[root_i]; - } - skeleton->roots = roots_array; + + skeleton->roots = roots; if (roots.size() == 0) { return FAILED; @@ -4312,12 +4338,12 @@ Error GLTFDocument::_create_skeletons(Ref state) { for (GLTFSkeletonIndex skel_i = 0; skel_i < state->skeletons.size(); ++skel_i) { Ref gltf_skeleton = state->skeletons.write[skel_i]; - Skeleton *skeleton = memnew(Skeleton); - gltf_skeleton->pandemonium_skeleton = skeleton; + Skeleton3D *skeleton = memnew(Skeleton3D); + gltf_skeleton->godot_skeleton = skeleton; state->skeleton3d_to_gltf_skeleton[skeleton->get_instance_id()] = skel_i; // Make a unique name, no gltf node represents this skeleton - skeleton->set_name(_gen_unique_name(state, "Skeleton")); + skeleton->set_name(_gen_unique_name(state, "Skeleton3D")); List bones; @@ -4329,7 +4355,7 @@ Error GLTFDocument::_create_skeletons(Ref state) { // a sorted order, and DEPTH FIRST bones.sort(); - while (!bones.empty()) { + while (!bones.is_empty()) { const GLTFNodeIndex node_i = bones.front()->get(); bones.pop_front(); @@ -4354,7 +4380,7 @@ Error GLTFDocument::_create_skeletons(Ref state) { const int bone_index = skeleton->get_bone_count(); - if (node->get_name().empty()) { + if (node->get_name().is_empty()) { node->set_name("bone"); } @@ -4362,6 +4388,9 @@ Error GLTFDocument::_create_skeletons(Ref state) { skeleton->add_bone(node->get_name()); skeleton->set_bone_rest(bone_index, node->xform); + skeleton->set_bone_pose_position(bone_index, node->position); + skeleton->set_bone_pose_rotation(bone_index, node->rotation.normalized()); + skeleton->set_bone_pose_scale(bone_index, node->scale); if (node->parent >= 0 && state->nodes[node->parent]->skeleton == skel_i) { const int bone_parent = skeleton->find_bone(state->nodes[node->parent]->get_name()); @@ -4388,7 +4417,7 @@ Error GLTFDocument::_map_skin_joints_indices_to_skeleton_bone_indices(Refjoints_original[joint_index]; const Ref node = state->nodes[node_i]; - const int bone_index = skeleton->pandemonium_skeleton->find_bone(node->get_name()); + 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); @@ -4422,16 +4451,16 @@ Error GLTFDocument::_create_skins(Ref state) { Ref gltf_skin = state->skins.write[skin_i]; Ref skin; - skin.instance(); + skin.instantiate(); // Some skins don't have IBM's! What absolute monsters! - const bool has_ibms = !gltf_skin->inverse_binds.empty(); + 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; + Transform3D xform; if (has_ibms) { xform = gltf_skin->inverse_binds[joint_i]; } @@ -4444,7 +4473,7 @@ Error GLTFDocument::_create_skins(Ref state) { } } - gltf_skin->pandemonium_skin = skin; + gltf_skin->godot_skin = skin; } // Purge the duplicates! @@ -4452,8 +4481,8 @@ Error GLTFDocument::_create_skins(Ref state) { // Create unique names now, after removing duplicates for (GLTFSkinIndex skin_i = 0; skin_i < state->skins.size(); ++skin_i) { - Ref skin = state->skins.write[skin_i]->pandemonium_skin; - if (skin->get_name().empty()) { + Ref 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")); } @@ -4475,8 +4504,8 @@ bool GLTFDocument::_skins_are_same(const Ref skin_a, const Ref skin_ return false; } - Transform a_xform = skin_a->get_bind_pose(i); - Transform b_xform = skin_b->get_bind_pose(i); + Transform3D a_xform = skin_a->get_bind_pose(i); + Transform3D b_xform = skin_b->get_bind_pose(i); if (a_xform != b_xform) { return false; @@ -4489,18 +4518,21 @@ bool GLTFDocument::_skins_are_same(const Ref skin_a, const Ref skin_ void GLTFDocument::_remove_duplicate_skins(Ref state) { for (int i = 0; i < state->skins.size(); ++i) { for (int j = i + 1; j < state->skins.size(); ++j) { - const Ref skin_i = state->skins[i]->pandemonium_skin; - const Ref skin_j = state->skins[j]->pandemonium_skin; + const Ref skin_i = state->skins[i]->godot_skin; + const Ref 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]->pandemonium_skin = skin_i; + state->skins.write[j]->godot_skin = skin_i; } } } } Error GLTFDocument::_serialize_lights(Ref state) { + if (state->lights.is_empty()) { + return OK; + } Array lights; for (GLTFLightIndex i = 0; i < state->lights.size(); i++) { Dictionary d; @@ -4511,8 +4543,8 @@ Error GLTFDocument::_serialize_lights(Ref state) { color[1] = light->color.g; color[2] = light->color.b; d["color"] = color; - d["type"] = light->type; - if (light->type == "spot") { + d["type"] = light->light_type; + if (light->light_type == "spot") { Dictionary s; float inner_cone_angle = light->inner_cone_angle; s["innerConeAngle"] = inner_cone_angle; @@ -4527,10 +4559,6 @@ Error GLTFDocument::_serialize_lights(Ref state) { lights.push_back(d); } - if (!state->lights.size()) { - return OK; - } - Dictionary extensions; if (state->json.has("extensions")) { extensions = state->json["extensions"]; @@ -4558,16 +4586,16 @@ Error GLTFDocument::_serialize_cameras(Ref state) { 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(); + og["zfar"] = camera->get_depth_far(); + og["znear"] = camera->get_depth_near(); d["orthographic"] = og; d["type"] = "orthographic"; } else if (camera->get_perspective()) { Dictionary ppt; - // GLTF spec is in radians, Pandemonium's camera is in degrees. + // 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(); + ppt["zfar"] = camera->get_depth_far(); + ppt["znear"] = camera->get_depth_near(); d["perspective"] = ppt; d["type"] = "perspective"; } @@ -4604,15 +4632,15 @@ Error GLTFDocument::_parse_lights(Ref state) { const Dictionary &d = lights[light_i]; Ref light; - light.instance(); + light.instantiate(); ERR_FAIL_COND_V(!d.has("type"), ERR_PARSE_ERROR); const String &type = d["type"]; - light->type = type; + light->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(); + const Color c = Color(arr[0], arr[1], arr[2]).linear_to_srgb(); light->color = c; } if (d.has("intensity")) { @@ -4649,17 +4677,17 @@ Error GLTFDocument::_parse_cameras(Ref state) { const Dictionary &d = cameras[i]; Ref camera; - camera.instance(); + camera.instantiate(); 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, Pandemonium's camera is in degrees. + // 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"]); + camera->set_depth_far(og["zfar"]); + camera->set_depth_near(og["znear"]); } else { camera->set_fov_size(10); } @@ -4667,15 +4695,15 @@ Error GLTFDocument::_parse_cameras(Ref state) { camera->set_perspective(true); if (d.has("perspective")) { const Dictionary &ppt = d["perspective"]; - // GLTF spec is in radians, Pandemonium's camera is in degrees. + // 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"]); + camera->set_depth_far(ppt["zfar"]); + camera->set_depth_near(ppt["znear"]); } else { camera->set_fov_size(10); } } else { - ERR_FAIL_V_MSG(ERR_PARSE_ERROR, "Camera should be in 'orthographic' or 'perspective'"); + ERR_FAIL_V_MSG(ERR_PARSE_ERROR, "Camera3D should be in 'orthographic' or 'perspective'"); } state->cameras.push_back(camera); @@ -4723,30 +4751,30 @@ Error GLTFDocument::_serialize_animations(Ref state) { continue; } - if (!gltf_animation->get_name().empty()) { + if (!gltf_animation->get_name().is_empty()) { d["name"] = gltf_animation->get_name(); } Array channels; Array samplers; - for (Map::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()) { + for (KeyValue &track_i : gltf_animation->get_tracks()) { + GLTFAnimation::Track track = track_i.value; + if (track.position_track.times.size()) { Dictionary t; t["sampler"] = samplers.size(); Dictionary s; - s["interpolation"] = interpolation_to_string(track.translation_track.interpolation); - Vector times = Variant(track.translation_track.times); + s["interpolation"] = interpolation_to_string(track.position_track.interpolation); + Vector times = Variant(track.position_track.times); s["input"] = _encode_accessor_as_floats(state, times, false); - Vector values = Variant(track.translation_track.values); + Vector values = Variant(track.position_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(); + target["node"] = track_i.key; t["target"] = target; channels.push_back(t); @@ -4759,14 +4787,14 @@ Error GLTFDocument::_serialize_animations(Ref state) { s["interpolation"] = interpolation_to_string(track.rotation_track.interpolation); Vector times = Variant(track.rotation_track.times); s["input"] = _encode_accessor_as_floats(state, times, false); - Vector values = track.rotation_track.values; - s["output"] = _encode_accessor_as_quats(state, values, false); + Vector values = track.rotation_track.values; + s["output"] = _encode_accessor_as_quaternions(state, values, false); samplers.push_back(s); Dictionary target; target["path"] = "rotation"; - target["node"] = track_i->key(); + target["node"] = track_i.key; t["target"] = target; channels.push_back(t); @@ -4786,7 +4814,7 @@ Error GLTFDocument::_serialize_animations(Ref state) { Dictionary target; target["path"] = "scale"; - target["node"] = track_i->key(); + target["node"] = track_i.key; t["target"] = target; channels.push_back(t); @@ -4825,7 +4853,7 @@ Error GLTFDocument::_serialize_animations(Ref state) { bool last = false; Vector weight_track; while (true) { - float weight = _interpolate_track(track.weight_tracks[track_idx].times, + float weight = _interpolate_track(track.weight_tracks[track_idx].times, track.weight_tracks[track_idx].values, time, track.weight_tracks[track_idx].interpolation); @@ -4849,7 +4877,7 @@ Error GLTFDocument::_serialize_animations(Ref state) { int32_t weight_tracks_size = track.weight_tracks.size(); all_track_values.resize(weight_tracks_size * values_size); for (int k = 0; k < track.weight_tracks.size(); k++) { - Vector wdata = track.weight_tracks[k].values; + Vector wdata = track.weight_tracks[k].values; for (int l = 0; l < wdata.size(); l++) { int32_t index = l * weight_tracks_size + k; ERR_BREAK(index >= all_track_values.size()); @@ -4865,7 +4893,7 @@ Error GLTFDocument::_serialize_animations(Ref state) { Dictionary target; target["path"] = "weights"; - target["node"] = track_i->key(); + target["node"] = track_i.key; t["target"] = target; channels.push_back(t); @@ -4899,7 +4927,7 @@ Error GLTFDocument::_parse_animations(Ref state) { const Dictionary &d = animations[i]; Ref animation; - animation.instance(); + animation.instantiate(); if (!d.has("channels") || !d.has("samplers")) { continue; @@ -4910,14 +4938,11 @@ Error GLTFDocument::_parse_animations(Ref state) { 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")) { + const String name_lower = name.to_lower(); + if (name_lower.begins_with("loop") || name_lower.ends_with("loop") || name_lower.begins_with("cycle") || name_lower.ends_with("cycle")) { animation->set_loop(true); } - if (state->use_legacy_names) { - animation->set_name(_sanitize_scene_name(state, name)); - } else { - animation->set_name(_gen_unique_animation_name(state, name)); - } + animation->set_name(_gen_unique_animation_name(state, name)); } for (int j = 0; j < channels.size(); j++) { @@ -4975,12 +5000,12 @@ Error GLTFDocument::_parse_animations(Ref state) { const Vector times = _decode_accessor_as_floats(state, input, false); if (path == "translation") { - const Vector 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 + const Vector positions = _decode_accessor_as_vec3(state, output, false); + track->position_track.interpolation = interp; + track->position_track.times = Variant(times); //convert via variant + track->position_track.values = Variant(positions); //convert via variant } else if (path == "rotation") { - const Vector rotations = _decode_accessor_as_quat(state, output, false); + const Vector rotations = _decode_accessor_as_quaternion(state, output, false); track->rotation_track.interpolation = interp; track->rotation_track.times = Variant(times); //convert via variant track->rotation_track.values = rotations; @@ -5004,10 +5029,10 @@ Error GLTFDocument::_parse_animations(Ref state) { 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 cf; + GLTFAnimation::Channel cf; cf.interpolation = interp; cf.times = Variant(times); - Vector wdata; + Vector wdata; wdata.resize(wlen); for (int l = 0; l < wlen; l++) { wdata.write[l] = weights[l * wc + k]; @@ -5038,11 +5063,11 @@ void GLTFDocument::_assign_scene_names(Ref state) { continue; } - if (n->get_name().empty()) { + 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, "Camera")); + n->set_name(_gen_unique_name(state, "Camera3D")); } else { n->set_name(_gen_unique_name(state, "Node")); } @@ -5050,16 +5075,12 @@ void GLTFDocument::_assign_scene_names(Ref state) { n->set_name(_gen_unique_name(state, n->get_name())); } - - // Assign a unique name to the scene last to avoid naming conflicts with the root - state->scene_name = _gen_unique_name(state, state->scene_name); } -BoneAttachment *GLTFDocument::_generate_bone_attachment(Ref state, Skeleton *skeleton, const GLTFNodeIndex node_index, const GLTFNodeIndex bone_index) { +BoneAttachment3D *GLTFDocument::_generate_bone_attachment(Ref state, Skeleton3D *skeleton, const GLTFNodeIndex node_index, const GLTFNodeIndex bone_index) { Ref gltf_node = state->nodes[node_index]; Ref bone_node = state->nodes[bone_index]; - - BoneAttachment *bone_attachment = memnew(BoneAttachment); + BoneAttachment3D *bone_attachment = memnew(BoneAttachment3D); print_verbose("glTF: Creating bone attachment for: " + gltf_node->get_name()); ERR_FAIL_COND_V(!bone_node->joint, nullptr); @@ -5069,49 +5090,28 @@ BoneAttachment *GLTFDocument::_generate_bone_attachment(Ref state, Sk return bone_attachment; } -GLTFMeshIndex GLTFDocument::_convert_mesh_to_gltf(Ref state, MeshInstance *p_mesh_instance) { +GLTFMeshIndex GLTFDocument::_convert_mesh_to_gltf(Ref state, MeshInstance3D *p_mesh_instance) { ERR_FAIL_NULL_V(p_mesh_instance, -1); if (p_mesh_instance->get_mesh().is_null()) { return -1; } - Ref import_mesh; - import_mesh.instance(); - Ref pandemonium_mesh = p_mesh_instance->get_mesh(); - if (pandemonium_mesh.is_null()) { - return -1; - } - int32_t blend_count = pandemonium_mesh->get_blend_shape_count(); + + Ref import_mesh = p_mesh_instance->get_mesh(); + Ref current_mesh = _mesh_to_importer_mesh(import_mesh); Vector blend_weights; + int32_t blend_count = import_mesh->get_blend_shape_count(); blend_weights.resize(blend_count); - Ref am = pandemonium_mesh; - if (am != nullptr) { - import_mesh = am; - } else { - for (int32_t surface_i = 0; surface_i < pandemonium_mesh->get_surface_count(); surface_i++) { - Mesh::PrimitiveType primitive_type = pandemonium_mesh->surface_get_primitive_type(surface_i); - Array arrays = pandemonium_mesh->surface_get_arrays(surface_i); - Ref mat = pandemonium_mesh->surface_get_material(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(); - } - int32_t mat_idx = import_mesh->get_surface_count(); - import_mesh->add_surface_from_arrays(primitive_type, arrays); - import_mesh->surface_set_material(mat_idx, mat); - } - } for (int32_t blend_i = 0; blend_i < blend_count; blend_i++) { blend_weights.write[blend_i] = 0.0f; } + Ref gltf_mesh; - gltf_mesh.instance(); + gltf_mesh.instantiate(); Array instance_materials; - for (int32_t surface_i = 0; surface_i < import_mesh->get_surface_count(); surface_i++) { - Ref mat = import_mesh->surface_get_material(surface_i); - if (p_mesh_instance->get_surface_material(surface_i).is_valid()) { - mat = p_mesh_instance->get_surface_material(surface_i); + for (int32_t surface_i = 0; surface_i < current_mesh->get_surface_count(); surface_i++) { + Ref mat = current_mesh->get_surface_material(surface_i); + if (p_mesh_instance->get_surface_override_material(surface_i).is_valid()) { + mat = p_mesh_instance->get_surface_override_material(surface_i); } if (p_mesh_instance->get_material_override().is_valid()) { mat = p_mesh_instance->get_material_override(); @@ -5119,37 +5119,34 @@ GLTFMeshIndex GLTFDocument::_convert_mesh_to_gltf(Ref state, MeshInst instance_materials.append(mat); } gltf_mesh->set_instance_materials(instance_materials); - gltf_mesh->set_mesh(import_mesh); + gltf_mesh->set_mesh(current_mesh); gltf_mesh->set_blend_weights(blend_weights); GLTFMeshIndex mesh_i = state->meshes.size(); state->meshes.push_back(gltf_mesh); return mesh_i; } -Spatial *GLTFDocument::_generate_mesh_instance(Ref state, Node *scene_parent, const GLTFNodeIndex node_index) { +ImporterMeshInstance3D *GLTFDocument::_generate_mesh_instance(Ref state, const GLTFNodeIndex node_index) { Ref gltf_node = state->nodes[node_index]; ERR_FAIL_INDEX_V(gltf_node->mesh, state->meshes.size(), nullptr); - MeshInstance *mi = memnew(MeshInstance); + ImporterMeshInstance3D *mi = memnew(ImporterMeshInstance3D); print_verbose("glTF: Creating mesh for: " + gltf_node->get_name()); Ref mesh = state->meshes.write[gltf_node->mesh]; if (mesh.is_null()) { return mi; } - Ref import_mesh = mesh->get_mesh(); + Ref 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; } -Spatial *GLTFDocument::_generate_light(Ref state, Node *scene_parent, const GLTFNodeIndex node_index) { +Node3D *GLTFDocument::_generate_light(Ref state, const GLTFNodeIndex node_index) { Ref gltf_node = state->nodes[node_index]; ERR_FAIL_INDEX_V(gltf_node->light, state->lights.size(), nullptr); @@ -5166,102 +5163,98 @@ Spatial *GLTFDocument::_generate_light(Ref state, Node *scene_parent, intensity /= 100; } - if (l->type == "directional") { - DirectionalLight *light = memnew(DirectionalLight); - light->set_param(Light::PARAM_ENERGY, intensity); + if (l->light_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); - if (l->type == "point") { - OmniLight *light = memnew(OmniLight); - light->set_param(OmniLight::PARAM_ENERGY, intensity); - light->set_param(OmniLight::PARAM_RANGE, range); + if (l->light_type == "point") { + OmniLight3D *light = memnew(OmniLight3D); + light->set_param(OmniLight3D::PARAM_ENERGY, intensity); + light->set_param(OmniLight3D::PARAM_RANGE, range); light->set_color(l->color); return light; } - if (l->type == "spot") { - SpotLight *light = memnew(SpotLight); - light->set_param(SpotLight::PARAM_ENERGY, intensity); - light->set_param(SpotLight::PARAM_RANGE, range); - light->set_param(SpotLight::PARAM_SPOT_ANGLE, Math::rad2deg(l->outer_cone_angle)); + if (l->light_type == "spot") { + SpotLight3D *light = memnew(SpotLight3D); + light->set_param(SpotLight3D::PARAM_ENERGY, intensity); + 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(SpotLight::PARAM_SPOT_ATTENUATION, angle_attenuation); + light->set_param(SpotLight3D::PARAM_SPOT_ATTENUATION, angle_attenuation); return light; } - return memnew(Spatial); + return memnew(Node3D); } -Camera *GLTFDocument::_generate_camera(Ref state, Node *scene_parent, const GLTFNodeIndex node_index) { +Camera3D *GLTFDocument::_generate_camera(Ref state, const GLTFNodeIndex node_index) { Ref gltf_node = state->nodes[node_index]; ERR_FAIL_INDEX_V(gltf_node->camera, state->cameras.size(), nullptr); - Camera *camera = memnew(Camera); + Camera3D *camera = memnew(Camera3D); print_verbose("glTF: Creating camera for: " + gltf_node->get_name()); Ref c = state->cameras[gltf_node->camera]; if (c->get_perspective()) { - camera->set_perspective(c->get_fov_size(), c->get_znear(), c->get_zfar()); + camera->set_perspective(c->get_fov_size(), c->get_depth_near(), c->get_depth_far()); } else { - camera->set_orthogonal(c->get_fov_size(), c->get_znear(), c->get_zfar()); + camera->set_orthogonal(c->get_fov_size(), c->get_depth_near(), c->get_depth_far()); } return camera; } -GLTFCameraIndex GLTFDocument::_convert_camera(Ref state, Camera *p_camera) { +GLTFCameraIndex GLTFDocument::_convert_camera(Ref state, Camera3D *p_camera) { print_verbose("glTF: Converting camera: " + p_camera->get_name()); Ref c; - c.instance(); + c.instantiate(); - if (p_camera->get_projection() == Camera::Projection::PROJECTION_PERSPECTIVE) { + if (p_camera->get_projection() == Camera3D::ProjectionType::PROJECTION_PERSPECTIVE) { c->set_perspective(true); - c->set_fov_size(p_camera->get_fov()); - c->set_zfar(p_camera->get_zfar()); - c->set_znear(p_camera->get_znear()); - } else { - c->set_fov_size(p_camera->get_fov()); - c->set_zfar(p_camera->get_zfar()); - c->set_znear(p_camera->get_znear()); } + c->set_fov_size(p_camera->get_fov()); + c->set_depth_far(p_camera->get_far()); + c->set_depth_near(p_camera->get_near()); GLTFCameraIndex camera_index = state->cameras.size(); state->cameras.push_back(c); return camera_index; } -GLTFLightIndex GLTFDocument::_convert_light(Ref state, Light *p_light) { +GLTFLightIndex GLTFDocument::_convert_light(Ref state, Light3D *p_light) { print_verbose("glTF: Converting light: " + p_light->get_name()); Ref l; - l.instance(); + l.instantiate(); l->color = p_light->get_color(); - if (cast_to(p_light)) { - l->type = "directional"; - DirectionalLight *light = cast_to(p_light); - l->intensity = light->get_param(DirectionalLight::PARAM_ENERGY); - l->range = FLT_MAX; // Range for directional lights is infinite in Pandemonium. - } else if (cast_to(p_light)) { - l->type = "point"; - OmniLight *light = cast_to(p_light); - l->range = light->get_param(OmniLight::PARAM_RANGE); - l->intensity = light->get_param(OmniLight::PARAM_ENERGY); - } else if (cast_to(p_light)) { - l->type = "spot"; - SpotLight *light = cast_to(p_light); - l->range = light->get_param(SpotLight::PARAM_RANGE); - l->intensity = light->get_param(SpotLight::PARAM_ENERGY); - l->outer_cone_angle = Math::deg2rad(light->get_param(SpotLight::PARAM_SPOT_ANGLE)); + if (cast_to(p_light)) { + l->light_type = "directional"; + DirectionalLight3D *light = cast_to(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(p_light)) { + l->light_type = "point"; + OmniLight3D *light = cast_to(p_light); + l->range = light->get_param(OmniLight3D::PARAM_RANGE); + l->intensity = light->get_param(OmniLight3D::PARAM_ENERGY); + } else if (cast_to(p_light)) { + l->light_type = "spot"; + SpotLight3D *light = cast_to(p_light); + l->range = light->get_param(SpotLight3D::PARAM_RANGE); + l->intensity = light->get_param(SpotLight3D::PARAM_ENERGY); + 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(SpotLight::PARAM_SPOT_ATTENUATION))); + 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; } @@ -5271,17 +5264,17 @@ GLTFLightIndex GLTFDocument::_convert_light(Ref state, Light *p_light return light_index; } -void GLTFDocument::_convert_spatial(Ref state, Spatial *p_spatial, Ref p_node) { - Transform xform = p_spatial->get_transform(); +void GLTFDocument::_convert_spatial(Ref state, Node3D *p_spatial, Ref p_node) { + Transform3D 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; + p_node->rotation = xform.basis.get_rotation_quaternion(); + p_node->position = xform.origin; } -Spatial *GLTFDocument::_generate_spatial(Ref state, Node *scene_parent, const GLTFNodeIndex node_index) { +Node3D *GLTFDocument::_generate_spatial(Ref state, const GLTFNodeIndex node_index) { Ref gltf_node = state->nodes[node_index]; - Spatial *spatial = memnew(Spatial); + Node3D *spatial = memnew(Node3D); print_verbose("glTF: Converting spatial: " + gltf_node->get_name()); return spatial; @@ -5293,32 +5286,44 @@ void GLTFDocument::_convert_scene_node(Ref state, Node *p_current, co return; } Ref gltf_node; - gltf_node.instance(); + gltf_node.instantiate(); gltf_node->set_name(_gen_unique_name(state, p_current->get_name())); - if (cast_to(p_current)) { - Spatial *spatial = cast_to(p_current); + if (cast_to(p_current)) { + Node3D *spatial = cast_to(p_current); _convert_spatial(state, spatial, gltf_node); } - if (cast_to(p_current)) { - MeshInstance *mi = cast_to(p_current); + if (cast_to(p_current)) { + MeshInstance3D *mi = cast_to(p_current); _convert_mesh_instance_to_gltf(mi, state, gltf_node); - } else if (cast_to(p_current)) { - BoneAttachment *bone = cast_to(p_current); + } else if (cast_to(p_current)) { + BoneAttachment3D *bone = cast_to(p_current); _convert_bone_attachment_to_gltf(bone, state, p_gltf_parent, p_gltf_root, gltf_node); return; - } else if (cast_to(p_current)) { - Skeleton *skel = cast_to(p_current); + } else if (cast_to(p_current)) { + Skeleton3D *skel = cast_to(p_current); _convert_skeleton_to_gltf(skel, state, p_gltf_parent, p_gltf_root, gltf_node); - // We ignore the Pandemonium Engine node that is the skeleton. + // We ignore the Godot Engine node that is the skeleton. return; - } else if (cast_to(p_current)) { - MultiMeshInstance *multi = cast_to(p_current); - _convert_mult_mesh_instance_to_gltf(multi, p_gltf_parent, p_gltf_root, gltf_node, state); - } else if (cast_to(p_current)) { - Camera *camera = Object::cast_to(p_current); + } else if (cast_to(p_current)) { + MultiMeshInstance3D *multi = cast_to(p_current); + _convert_multi_mesh_instance_to_gltf(multi, p_gltf_parent, p_gltf_root, gltf_node, state); +#ifdef MODULE_CSG_ENABLED + } else if (cast_to(p_current)) { + CSGShape3D *shape = cast_to(p_current); + if (shape->get_parent() && shape->is_root_shape()) { + _convert_csg_shape_to_gltf(shape, p_gltf_parent, gltf_node, state); + } +#endif // MODULE_CSG_ENABLED +#ifdef MODULE_GRIDMAP_ENABLED + } else if (cast_to(p_current)) { + GridMap *gridmap = Object::cast_to(p_current); + _convert_grid_map_to_gltf(gridmap, p_gltf_parent, p_gltf_root, gltf_node, state); +#endif // MODULE_GRIDMAP_ENABLED + } else if (cast_to(p_current)) { + Camera3D *camera = Object::cast_to(p_current); _convert_camera_to_gltf(camera, state, gltf_node); - } else if (cast_to(p_current)) { - Light *light = Object::cast_to(p_current); + } else if (cast_to(p_current)) { + Light3D *light = Object::cast_to(p_current); _convert_light_to_gltf(light, state, gltf_node); } else if (cast_to(p_current)) { AnimationPlayer *animation_player = Object::cast_to(p_current); @@ -5338,6 +5343,47 @@ void GLTFDocument::_convert_scene_node(Ref state, Node *p_current, co } } +#ifdef MODULE_CSG_ENABLED +void GLTFDocument::_convert_csg_shape_to_gltf(CSGShape3D *p_current, GLTFNodeIndex p_gltf_parent, Ref gltf_node, Ref state) { + CSGShape3D *csg = p_current; + csg->call("_update_shape"); + Array meshes = csg->get_meshes(); + if (meshes.size() != 2) { + return; + } + + Ref mesh; + mesh.instantiate(); + { + Ref csg_mesh = csg->get_meshes()[1]; + + for (int32_t surface_i = 0; surface_i < csg_mesh->get_surface_count(); surface_i++) { + Array array = csg_mesh->surface_get_arrays(surface_i); + Ref mat = csg_mesh->surface_get_material(surface_i); + String mat_name; + if (mat.is_valid()) { + mat_name = mat->get_name(); + } else { + // Assign default material when no material is assigned. + mat = Ref(memnew(StandardMaterial3D)); + } + mesh->add_surface(csg_mesh->surface_get_primitive_type(surface_i), + array, csg_mesh->surface_get_blend_shape_arrays(surface_i), csg_mesh->surface_get_lods(surface_i), mat, + mat_name, csg_mesh->surface_get_format(surface_i)); + } + } + + Ref gltf_mesh; + gltf_mesh.instantiate(); + gltf_mesh->set_mesh(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())); +} +#endif // MODULE_CSG_ENABLED + void GLTFDocument::_create_gltf_node(Ref state, Node *p_scene_parent, GLTFNodeIndex current_node_i, GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_gltf_node, Ref gltf_node) { state->scene_nodes.insert(current_node_i, p_scene_parent); @@ -5358,7 +5404,7 @@ void GLTFDocument::_convert_animation_player_to_gltf(AnimationPlayer *animation_ void GLTFDocument::_check_visibility(Node *p_node, bool &retflag) { retflag = true; - Spatial *spatial = Object::cast_to(p_node); + Node3D *spatial = Object::cast_to(p_node); Node2D *node_2d = Object::cast_to(p_node); if (node_2d && !node_2d->is_visible()) { return; @@ -5369,7 +5415,7 @@ void GLTFDocument::_check_visibility(Node *p_node, bool &retflag) { retflag = false; } -void GLTFDocument::_convert_camera_to_gltf(Camera *camera, Ref state, Ref gltf_node) { +void GLTFDocument::_convert_camera_to_gltf(Camera3D *camera, Ref state, Ref gltf_node) { ERR_FAIL_COND(!camera); GLTFCameraIndex camera_index = _convert_camera(state, camera); if (camera_index != -1) { @@ -5377,7 +5423,7 @@ void GLTFDocument::_convert_camera_to_gltf(Camera *camera, Ref state, } } -void GLTFDocument::_convert_light_to_gltf(Light *light, Ref state, Ref gltf_node) { +void GLTFDocument::_convert_light_to_gltf(Light3D *light, Ref state, Ref gltf_node) { ERR_FAIL_COND(!light); GLTFLightIndex light_index = _convert_light(state, light); if (light_index != -1) { @@ -5385,58 +5431,112 @@ void GLTFDocument::_convert_light_to_gltf(Light *light, Ref state, Re } } -void GLTFDocument::_convert_mult_mesh_instance_to_gltf(MultiMeshInstance *p_multi_mesh_instance, GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_node_index, Ref gltf_node, Ref state) { +#ifdef MODULE_GRIDMAP_ENABLED +void GLTFDocument::_convert_grid_map_to_gltf(GridMap *p_grid_map, GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_node_index, Ref gltf_node, Ref state) { + Array cells = p_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 = p_grid_map->get_cell_item( + Vector3(cell_location.x, cell_location.y, cell_location.z)); + Transform3D cell_xform; + cell_xform.basis.set_orthogonal_index( + p_grid_map->get_cell_item_orientation( + Vector3(cell_location.x, cell_location.y, cell_location.z))); + cell_xform.basis.scale(Vector3(p_grid_map->get_cell_scale(), + p_grid_map->get_cell_scale(), + p_grid_map->get_cell_scale())); + cell_xform.set_origin(p_grid_map->map_to_world( + Vector3(cell_location.x, cell_location.y, cell_location.z))); + Ref gltf_mesh; + gltf_mesh.instantiate(); + gltf_mesh->set_mesh(_mesh_to_importer_mesh(p_grid_map->get_mesh_library()->get_item_mesh(cell))); + new_gltf_node->mesh = state->meshes.size(); + state->meshes.push_back(gltf_mesh); + new_gltf_node->xform = cell_xform * p_grid_map->get_transform(); + new_gltf_node->set_name(_gen_unique_name(state, p_grid_map->get_mesh_library()->get_item_name(cell))); + } +} +#endif // MODULE_GRIDMAP_ENABLED + +void GLTFDocument::_convert_multi_mesh_instance_to_gltf( + MultiMeshInstance3D *p_multi_mesh_instance, + GLTFNodeIndex p_parent_node_index, + GLTFNodeIndex p_root_node_index, + Ref gltf_node, Ref state) { + ERR_FAIL_COND(!p_multi_mesh_instance); Ref multi_mesh = p_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(Vector3(0, 1, 0), rotation); - Size2 scale = xform_2d.get_scale(); - transform.basis.set_quat_scale(quat, - Vector3(scale.x, 0, scale.y)); - transform = - p_multi_mesh_instance->get_transform() * transform; - } else if (multi_mesh->get_transform_format() == MultiMesh::TRANSFORM_3D) { - transform = p_multi_mesh_instance->get_transform() * - multi_mesh->get_instance_transform(instance_i); - } - Ref mm = multi_mesh->get_mesh(); - if (mm.is_valid()) { - Ref 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_from_arrays(mm->surface_get_primitive_type(surface_i), surface); - } - Ref 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, p_multi_mesh_instance->get_name())); - gltf_node->children.push_back(state->nodes.size()); - state->nodes.push_back(new_gltf_node); + if (multi_mesh.is_null()) { + return; + } + Ref gltf_mesh; + gltf_mesh.instantiate(); + Ref mesh = multi_mesh->get_mesh(); + if (mesh.is_null()) { + return; + } + gltf_mesh->set_name(multi_mesh->get_name()); + Ref importer_mesh; + importer_mesh.instantiate(); + Ref array_mesh = multi_mesh->get_mesh(); + if (array_mesh.is_valid()) { + importer_mesh->set_blend_shape_mode(array_mesh->get_blend_shape_mode()); + for (int32_t blend_i = 0; blend_i < array_mesh->get_blend_shape_count(); blend_i++) { + importer_mesh->add_blend_shape(array_mesh->get_blend_shape_name(blend_i)); } } + for (int32_t surface_i = 0; surface_i < mesh->get_surface_count(); surface_i++) { + Ref mat = mesh->surface_get_material(surface_i); + String material_name; + if (mat.is_valid()) { + material_name = mat->get_name(); + } + Array blend_arrays; + if (array_mesh.is_valid()) { + blend_arrays = array_mesh->surface_get_blend_shape_arrays(surface_i); + } + importer_mesh->add_surface(mesh->surface_get_primitive_type(surface_i), mesh->surface_get_arrays(surface_i), + blend_arrays, mesh->surface_get_lods(surface_i), mat, material_name, mesh->surface_get_format(surface_i)); + } + gltf_mesh->set_mesh(importer_mesh); + GLTFMeshIndex mesh_index = state->meshes.size(); + state->meshes.push_back(gltf_mesh); + for (int32_t instance_i = 0; instance_i < multi_mesh->get_instance_count(); + instance_i++) { + Transform3D 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(); + Quaternion quaternion(Vector3(0, 1, 0), rotation); + Size2 scale = xform_2d.get_scale(); + transform.basis.set_quaternion_scale(quaternion, + Vector3(scale.x, 0, scale.y)); + transform = p_multi_mesh_instance->get_transform() * transform; + } else if (multi_mesh->get_transform_format() == MultiMesh::TRANSFORM_3D) { + transform = p_multi_mesh_instance->get_transform() * + multi_mesh->get_instance_transform(instance_i); + } + Ref new_gltf_node; + new_gltf_node.instantiate(); + new_gltf_node->mesh = mesh_index; + new_gltf_node->xform = transform; + new_gltf_node->set_name(_gen_unique_name(state, p_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(Skeleton *p_skeleton3d, Ref state, GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_node_index, Ref gltf_node) { - Skeleton *skeleton = p_skeleton3d; +void GLTFDocument::_convert_skeleton_to_gltf(Skeleton3D *p_skeleton3d, Ref state, GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_node_index, Ref gltf_node) { + Skeleton3D *skeleton = p_skeleton3d; Ref gltf_skeleton; - gltf_skeleton.instance(); + gltf_skeleton.instantiate(); // GLTFSkeleton is only used to hold internal state data. It will not be written to the document. // - gltf_skeleton->pandemonium_skeleton = skeleton; + gltf_skeleton->godot_skeleton = skeleton; GLTFSkeletonIndex skeleton_i = state->skeletons.size(); state->skeleton3d_to_gltf_skeleton[skeleton->get_instance_id()] = skeleton_i; state->skeletons.push_back(gltf_skeleton); @@ -5444,14 +5544,14 @@ void GLTFDocument::_convert_skeleton_to_gltf(Skeleton *p_skeleton3d, Refget_bone_count(); for (BoneId bone_i = 0; bone_i < bone_count; bone_i++) { Ref joint_node; - joint_node.instance(); + joint_node.instantiate(); // Note that we cannot use _gen_unique_bone_name here, because glTF spec requires all node // names to be unique regardless of whether or not they are used as joints. joint_node->set_name(_gen_unique_name(state, skeleton->get_bone_name(bone_i))); - Transform xform = skeleton->get_bone_rest(bone_i) * skeleton->get_bone_pose(bone_i); + Transform3D xform = skeleton->get_bone_pose(bone_i); joint_node->scale = xform.basis.get_scale(); - joint_node->rotation = xform.basis.get_rotation_quat(); - joint_node->translation = xform.origin; + joint_node->rotation = xform.basis.get_rotation_quaternion(); + joint_node->position = xform.origin; joint_node->joint = true; GLTFNodeIndex current_node_i = state->nodes.size(); state->scene_nodes.insert(current_node_i, skeleton); @@ -5461,10 +5561,10 @@ void GLTFDocument::_convert_skeleton_to_gltf(Skeleton *p_skeleton3d, Refget_bone_parent(bone_i) == -1) { gltf_skeleton->roots.push_back(current_node_i); } - gltf_skeleton->pandemonium_bone_node.insert(bone_i, current_node_i); + gltf_skeleton->godot_bone_node.insert(bone_i, current_node_i); } for (BoneId bone_i = 0; bone_i < bone_count; bone_i++) { - GLTFNodeIndex current_node_i = gltf_skeleton->pandemonium_bone_node[bone_i]; + GLTFNodeIndex current_node_i = gltf_skeleton->godot_bone_node[bone_i]; BoneId parent_bone_id = skeleton->get_bone_parent(bone_i); if (parent_bone_id == -1) { if (p_parent_node_index != -1) { @@ -5472,7 +5572,7 @@ void GLTFDocument::_convert_skeleton_to_gltf(Skeleton *p_skeleton3d, Refnodes.write[p_parent_node_index]->children.push_back(current_node_i); } } else { - GLTFNodeIndex parent_node_i = gltf_skeleton->pandemonium_bone_node[parent_bone_id]; + GLTFNodeIndex parent_node_i = gltf_skeleton->godot_bone_node[parent_bone_id]; state->nodes.write[current_node_i]->parent = parent_node_i; state->nodes.write[parent_node_i]->children.push_back(current_node_i); } @@ -5484,17 +5584,24 @@ void GLTFDocument::_convert_skeleton_to_gltf(Skeleton *p_skeleton3d, Ref state, GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_node_index, Ref gltf_node) { - Skeleton *skeleton; +void GLTFDocument::_convert_bone_attachment_to_gltf(BoneAttachment3D *p_bone_attachment, Ref state, GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_node_index, Ref gltf_node) { + Skeleton3D *skeleton; // Note that relative transforms to external skeletons and pose overrides are not supported. - skeleton = cast_to(p_bone_attachment->get_parent()); + if (p_bone_attachment->get_use_external_skeleton()) { + skeleton = cast_to(p_bone_attachment->get_node_or_null(p_bone_attachment->get_external_skeleton())); + } else { + skeleton = cast_to(p_bone_attachment->get_parent()); + } GLTFSkeletonIndex skel_gltf_i = -1; if (skeleton != nullptr && state->skeleton3d_to_gltf_skeleton.has(skeleton->get_instance_id())) { skel_gltf_i = state->skeleton3d_to_gltf_skeleton[skeleton->get_instance_id()]; } int bone_idx = -1; if (skeleton != nullptr) { - bone_idx = skeleton->find_bone(p_bone_attachment->get_bone_name()); + bone_idx = p_bone_attachment->get_bone_idx(); + if (bone_idx == -1) { + bone_idx = skeleton->find_bone(p_bone_attachment->get_bone_name()); + } } GLTFNodeIndex par_node_index = p_parent_node_index; if (skeleton != nullptr && bone_idx != -1 && skel_gltf_i != -1) { @@ -5508,14 +5615,14 @@ void GLTFDocument::_convert_bone_attachment_to_gltf(BoneAttachment *p_bone_attac } } -void GLTFDocument::_convert_mesh_instance_to_gltf(MeshInstance *p_scene_parent, Ref state, Ref gltf_node) { +void GLTFDocument::_convert_mesh_instance_to_gltf(MeshInstance3D *p_scene_parent, Ref state, Ref gltf_node) { GLTFMeshIndex gltf_mesh_index = _convert_mesh_to_gltf(state, p_scene_parent); if (gltf_mesh_index != -1) { gltf_node->mesh = gltf_mesh_index; } } -void GLTFDocument::_generate_scene_node(Ref state, Node *scene_parent, Spatial *scene_root, const GLTFNodeIndex node_index) { +void GLTFDocument::_generate_scene_node(Ref state, Node *scene_parent, Node3D *scene_root, const GLTFNodeIndex node_index) { Ref gltf_node = state->nodes[node_index]; if (gltf_node->skeleton >= 0) { @@ -5523,42 +5630,41 @@ void GLTFDocument::_generate_scene_node(Ref state, Node *scene_parent return; } - Spatial *current_node = nullptr; + Node3D *current_node = nullptr; // Is our parent a skeleton - Skeleton *active_skeleton = Object::cast_to(scene_parent); + Skeleton3D *active_skeleton = Object::cast_to(scene_parent); const bool non_bone_parented_to_skeleton = active_skeleton; - // If we have an active skeleton, and the node is node skinned, we need to create a bone attachment + // skinned meshes must not be placed in a bone attachment. if (non_bone_parented_to_skeleton && gltf_node->skin < 0) { // Bone Attachment - Parent Case - BoneAttachment *bone_attachment = _generate_bone_attachment(state, active_skeleton, node_index, gltf_node->parent); + BoneAttachment3D *bone_attachment = _generate_bone_attachment(state, active_skeleton, node_index, gltf_node->parent); - scene_parent->add_child(bone_attachment); + scene_parent->add_child(bone_attachment, true); 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, "BoneAttachment")); + 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; } if (gltf_node->mesh >= 0) { - current_node = _generate_mesh_instance(state, scene_parent, node_index); + current_node = _generate_mesh_instance(state, node_index); } else if (gltf_node->camera >= 0) { - current_node = _generate_camera(state, scene_parent, node_index); + current_node = _generate_camera(state, node_index); } else if (gltf_node->light >= 0) { - current_node = _generate_light(state, scene_parent, node_index); + current_node = _generate_light(state, node_index); } // We still have not managed to make a node. if (!current_node) { - current_node = _generate_spatial(state, scene_parent, node_index); + current_node = _generate_spatial(state, node_index); } - - scene_parent->add_child(current_node); + scene_parent->add_child(current_node, true); if (current_node != scene_root) { current_node->set_owner(scene_root); } @@ -5572,27 +5678,27 @@ void GLTFDocument::_generate_scene_node(Ref state, Node *scene_parent } } -void GLTFDocument::_generate_skeleton_bone_node(Ref state, Node *scene_parent, Spatial *scene_root, const GLTFNodeIndex node_index) { +void GLTFDocument::_generate_skeleton_bone_node(Ref state, Node *scene_parent, Node3D *scene_root, const GLTFNodeIndex node_index) { Ref gltf_node = state->nodes[node_index]; - Spatial *current_node = nullptr; + Node3D *current_node = nullptr; - Skeleton *skeleton = state->skeletons[gltf_node->skeleton]->pandemonium_skeleton; + Skeleton3D *skeleton = state->skeletons[gltf_node->skeleton]->godot_skeleton; // In this case, this node is already a bone in skeleton. const bool is_skinned_mesh = (gltf_node->skin >= 0 && gltf_node->mesh >= 0); const bool requires_extra_node = (gltf_node->mesh >= 0 || gltf_node->camera >= 0 || gltf_node->light >= 0); - Skeleton *active_skeleton = Object::cast_to(scene_parent); + Skeleton3D *active_skeleton = Object::cast_to(scene_parent); if (active_skeleton != skeleton) { if (active_skeleton) { // Bone Attachment - Direct Parented Skeleton Case - BoneAttachment *bone_attachment = _generate_bone_attachment(state, active_skeleton, node_index, gltf_node->parent); + BoneAttachment3D *bone_attachment = _generate_bone_attachment(state, active_skeleton, node_index, gltf_node->parent); - scene_parent->add_child(bone_attachment); + scene_parent->add_child(bone_attachment, true); 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, "BoneAttachment")); + 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 @@ -5602,7 +5708,7 @@ void GLTFDocument::_generate_skeleton_bone_node(Ref state, Node *scen // Add it to the scene if it has not already been added if (skeleton->get_parent() == nullptr) { - scene_parent->add_child(skeleton); + scene_parent->add_child(skeleton, true); skeleton->set_owner(scene_root); } } @@ -5614,13 +5720,13 @@ void GLTFDocument::_generate_skeleton_bone_node(Ref state, Node *scen // skinned meshes must not be placed in a bone attachment. if (!is_skinned_mesh) { // Bone Attachment - Same Node Case - BoneAttachment *bone_attachment = _generate_bone_attachment(state, active_skeleton, node_index, node_index); + BoneAttachment3D *bone_attachment = _generate_bone_attachment(state, active_skeleton, node_index, node_index); - scene_parent->add_child(bone_attachment); + scene_parent->add_child(bone_attachment, true); 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, "BoneAttachment")); + 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 @@ -5629,23 +5735,19 @@ void GLTFDocument::_generate_skeleton_bone_node(Ref state, Node *scen // We still have not managed to make a node if (gltf_node->mesh >= 0) { - current_node = _generate_mesh_instance(state, scene_parent, node_index); + current_node = _generate_mesh_instance(state, node_index); } else if (gltf_node->camera >= 0) { - current_node = _generate_camera(state, scene_parent, node_index); + current_node = _generate_camera(state, node_index); } else if (gltf_node->light >= 0) { - current_node = _generate_light(state, scene_parent, node_index); + current_node = _generate_light(state, node_index); } - scene_parent->add_child(current_node); + scene_parent->add_child(current_node, true); if (current_node != scene_root) { current_node->set_owner(scene_root); } // Do not set transform here. Transform is already applied to our bone. - if (state->use_legacy_names) { - current_node->set_name(_legacy_validate_node_name(gltf_node->get_name())); - } else { - current_node->set_name(gltf_node->get_name()); - } + current_node->set_name(gltf_node->get_name()); } state->scene_nodes.insert(node_index, current_node); @@ -5656,7 +5758,7 @@ void GLTFDocument::_generate_skeleton_bone_node(Ref state, Node *scen } template -struct EditorSceneImporterGLTFInterpolate { +struct SceneFormatImporterGLTFInterpolate { T lerp(const T &a, const T &b, float c) const { return a + (b - a) * c; } @@ -5682,31 +5784,31 @@ struct EditorSceneImporterGLTFInterpolate { // thank you for existing, partial specialization template <> -struct EditorSceneImporterGLTFInterpolate { - 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."); +struct SceneFormatImporterGLTFInterpolate { + Quaternion lerp(const Quaternion &a, const Quaternion &b, const float c) const { + ERR_FAIL_COND_V_MSG(!a.is_normalized(), Quaternion(), "The quaternion \"a\" must be normalized."); + ERR_FAIL_COND_V_MSG(!b.is_normalized(), Quaternion(), "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."); + Quaternion catmull_rom(const Quaternion &p0, const Quaternion &p1, const Quaternion &p2, const Quaternion &p3, const float c) { + ERR_FAIL_COND_V_MSG(!p1.is_normalized(), Quaternion(), "The quaternion \"p1\" must be normalized."); + ERR_FAIL_COND_V_MSG(!p2.is_normalized(), Quaternion(), "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."); + Quaternion bezier(const Quaternion start, const Quaternion control_1, const Quaternion control_2, const Quaternion end, const float t) { + ERR_FAIL_COND_V_MSG(!start.is_normalized(), Quaternion(), "The start quaternion must be normalized."); + ERR_FAIL_COND_V_MSG(!end.is_normalized(), Quaternion(), "The end quaternion must be normalized."); return start.slerp(end, t).normalized(); } }; template -T GLTFDocument::_interpolate_track(const Vector &p_times, const Vector &p_values, const float p_time, const GLTFAnimation::Interpolation p_interp) { +T GLTFDocument::_interpolate_track(const Vector &p_times, const Vector &p_values, const float p_time, const GLTFAnimation::Interpolation p_interp) { ERR_FAIL_COND_V(!p_values.size(), T()); if (p_times.size() != (p_values.size() / (p_interp == GLTFAnimation::INTERP_CUBIC_SPLINE ? 3 : 1))) { ERR_PRINT_ONCE("The interpolated values are not corresponding to its times."); @@ -5721,7 +5823,7 @@ T GLTFDocument::_interpolate_track(const Vector &p_times, const Vector idx++; } - EditorSceneImporterGLTFInterpolate interp; + SceneFormatImporterGLTFInterpolate interp; switch (p_interp) { case GLTFAnimation::INTERP_LINEAR: { @@ -5780,40 +5882,40 @@ void GLTFDocument::_import_animation(Ref state, AnimationPlayer *ap, Ref anim = state->animations[index]; String name = anim->get_name(); - if (name.empty()) { + 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.instance(); + animation.instantiate(); animation->set_name(name); if (anim->get_loop()) { - animation->set_loop(true); + animation->set_loop_mode(Animation::LOOP_LINEAR); } float length = 0.0; - for (Map::Element *track_i = anim->get_tracks().front(); track_i; track_i = track_i->next()) { - const GLTFAnimation::Track &track = track_i->get(); + for (const KeyValue &track_i : anim->get_tracks()) { + const GLTFAnimation::Track &track = track_i.value; //need to find the path: for skeletons, weight tracks will affect the mesh NodePath node_path; //for skeletons, transform tracks always affect bones NodePath transform_node_path; - GLTFNodeIndex node_index = track_i->key(); + GLTFNodeIndex node_index = track_i.key; - const Ref gltf_node = state->nodes[track_i->key()]; + const Ref gltf_node = state->nodes[track_i.key]; Node *root = ap->get_parent(); ERR_FAIL_COND(root == nullptr); - Map::Element *node_element = state->scene_nodes.find(node_index); - ERR_CONTINUE_MSG(node_element == nullptr, vformat("Unable to find node %d for animation", node_index)); - node_path = root->get_path_to(node_element->get()); + HashMap::Iterator node_element = state->scene_nodes.find(node_index); + ERR_CONTINUE_MSG(!node_element, vformat("Unable to find node %d for animation", node_index)); + node_path = root->get_path_to(node_element->value); if (gltf_node->skeleton >= 0) { - const Skeleton *sk = state->skeletons[gltf_node->skeleton]->pandemonium_skeleton; + const Skeleton3D *sk = state->skeletons[gltf_node->skeleton]->godot_skeleton; ERR_FAIL_COND(sk == nullptr); const String path = ap->get_parent()->get_path_to(sk); @@ -5826,8 +5928,8 @@ void GLTFDocument::_import_animation(Ref state, AnimationPlayer *ap, 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.position_track.times.size(); i++) { + length = MAX(length, track.position_track.times[i]); } for (int i = 0; i < track.scale_track.times.size(); i++) { length = MAX(length, track.scale_track.times[i]); @@ -5841,67 +5943,111 @@ void GLTFDocument::_import_animation(Ref state, AnimationPlayer *ap, // Animated TRS properties will not affect a skinned mesh. const bool transform_affects_skinned_mesh_instance = gltf_node->skeleton < 0 && gltf_node->skin >= 0; - if ((track.rotation_track.values.size() || track.translation_track.values.size() || track.scale_track.values.size()) && !transform_affects_skinned_mesh_instance) { + if ((track.rotation_track.values.size() || track.position_track.values.size() || track.scale_track.values.size()) && !transform_affects_skinned_mesh_instance) { //make transform track - int track_idx = animation->get_track_count(); - animation->add_track(Animation::TYPE_TRANSFORM); - animation->track_set_path(track_idx, transform_node_path); + int base_idx = animation->get_track_count(); + int position_idx = -1; + int rotation_idx = -1; + int scale_idx = -1; + + if (track.position_track.values.size()) { + Vector3 base_pos = state->nodes[track_i.key]->position; + bool not_default = false; //discard the track if all it contains is default values + for (int i = 0; i < track.position_track.times.size(); i++) { + Vector3 value = track.position_track.values[track.position_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i]; + if (!value.is_equal_approx(base_pos)) { + not_default = true; + break; + } + } + if (not_default) { + position_idx = base_idx; + animation->add_track(Animation::TYPE_POSITION_3D); + animation->track_set_path(position_idx, transform_node_path); + animation->track_set_imported(position_idx, true); //helps merging later + + base_idx++; + } + } + if (track.rotation_track.values.size()) { + Quaternion base_rot = state->nodes[track_i.key]->rotation.normalized(); + bool not_default = false; //discard the track if all it contains is default values + for (int i = 0; i < track.rotation_track.times.size(); i++) { + Quaternion value = track.rotation_track.values[track.rotation_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i].normalized(); + if (!value.is_equal_approx(base_rot)) { + not_default = true; + break; + } + } + if (not_default) { + rotation_idx = base_idx; + animation->add_track(Animation::TYPE_ROTATION_3D); + animation->track_set_path(rotation_idx, transform_node_path); + animation->track_set_imported(rotation_idx, true); //helps merging later + base_idx++; + } + } + if (track.scale_track.values.size()) { + Vector3 base_scale = state->nodes[track_i.key]->scale; + bool not_default = false; //discard the track if all it contains is default values + for (int i = 0; i < track.scale_track.times.size(); i++) { + Vector3 value = track.scale_track.values[track.scale_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i]; + if (!value.is_equal_approx(base_scale)) { + not_default = true; + break; + } + } + if (not_default) { + scale_idx = base_idx; + animation->add_track(Animation::TYPE_SCALE_3D); + animation->track_set_path(scale_idx, transform_node_path); + animation->track_set_imported(scale_idx, true); //helps merging later + base_idx++; + } + } + //first determine animation length const double increment = 1.0 / bake_fps; double time = 0.0; Vector3 base_pos; - Quat base_rot; + Quaternion 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 (rotation_idx == -1) { + base_rot = state->nodes[track_i.key]->rotation.normalized(); } - if (!track.translation_track.values.size()) { - base_pos = state->nodes[track_i->key()]->translation; + if (position_idx == -1) { + base_pos = state->nodes[track_i.key]->position; } - if (!track.scale_track.values.size()) { - base_scale = state->nodes[track_i->key()]->scale; + if (scale_idx == -1) { + base_scale = state->nodes[track_i.key]->scale; } bool last = false; while (true) { Vector3 pos = base_pos; - Quat rot = base_rot; + Quaternion rot = base_rot; Vector3 scale = base_scale; - if (track.translation_track.times.size()) { - pos = _interpolate_track(track.translation_track.times, track.translation_track.values, time, track.translation_track.interpolation); + if (position_idx >= 0) { + pos = _interpolate_track(track.position_track.times, track.position_track.values, time, track.position_track.interpolation); + animation->position_track_insert_key(position_idx, time, pos); } - if (track.rotation_track.times.size()) { - rot = _interpolate_track(track.rotation_track.times, track.rotation_track.values, time, track.rotation_track.interpolation); + if (rotation_idx >= 0) { + rot = _interpolate_track(track.rotation_track.times, track.rotation_track.values, time, track.rotation_track.interpolation); + animation->rotation_track_insert_key(rotation_idx, time, rot); } - if (track.scale_track.times.size()) { + if (scale_idx >= 0) { scale = _interpolate_track(track.scale_track.times, track.scale_track.values, time, track.scale_track.interpolation); + animation->scale_track_insert_key(scale_idx, time, scale); } - if (gltf_node->skeleton >= 0) { - Transform xform; - xform.basis.set_quat_scale(rot, scale); - xform.origin = pos; - - const Skeleton *skeleton = state->skeletons[gltf_node->skeleton]->pandemonium_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; } @@ -5918,15 +6064,15 @@ void GLTFDocument::_import_animation(Ref state, AnimationPlayer *ap, Ref mesh = state->meshes[gltf_node->mesh]; ERR_CONTINUE(mesh.is_null()); ERR_CONTINUE(mesh->get_mesh().is_null()); - const String prop = "blend_shapes/" + mesh->get_mesh()->get_blend_shape_name(i); + ERR_CONTINUE(mesh->get_mesh()->get_mesh().is_null()); - const String blend_path = String(node_path) + ":" + prop; + const String blend_path = String(node_path) + ":" + String(mesh->get_mesh()->get_blend_shape_name(i)); const int track_idx = animation->get_track_count(); - animation->add_track(Animation::TYPE_VALUE); + animation->add_track(Animation::TYPE_BLEND_SHAPE); animation->track_set_path(track_idx, blend_path); - // Only LINEAR and STEP (NEAREST) can be supported out of the box by Pandemonium's Animation, + // 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) { @@ -5934,7 +6080,7 @@ void GLTFDocument::_import_animation(Ref state, AnimationPlayer *ap, 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); + animation->blend_shape_track_insert_key(track_idx, t, attribs); } } else { // CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies. @@ -5942,7 +6088,8 @@ void GLTFDocument::_import_animation(Ref state, AnimationPlayer *ap, double time = 0.0; bool last = false; while (true) { - _interpolate_track(track.weight_tracks[i].times, track.weight_tracks[i].values, time, gltf_interp); + real_t blend = _interpolate_track(track.weight_tracks[i].times, track.weight_tracks[i].values, time, gltf_interp); + animation->blend_shape_track_insert_key(track_idx, time, blend); if (last) { break; } @@ -5958,7 +6105,14 @@ void GLTFDocument::_import_animation(Ref state, AnimationPlayer *ap, animation->set_length(length); - ap->add_animation(name, animation); + Ref library; + if (!ap->has_animation_library("")) { + library.instantiate(); + ap->add_animation_library("", library); + } else { + library = ap->get_animation_library(""); + } + library->add_animation(name, animation); } void GLTFDocument::_convert_mesh_instances(Ref state) { @@ -5968,18 +6122,20 @@ void GLTFDocument::_convert_mesh_instances(Ref state) { if (node->mesh < 0) { continue; } - Map::Element *mi_element = state->scene_nodes.find(mi_node_i); + HashMap::Iterator mi_element = state->scene_nodes.find(mi_node_i); if (!mi_element) { continue; } - MeshInstance *mi = Object::cast_to(mi_element->get()); - ERR_CONTINUE(!mi); - Transform mi_xform = mi->get_transform(); + MeshInstance3D *mi = Object::cast_to(mi_element->value); + if (!mi) { + continue; + } + Transform3D 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; + node->rotation = mi_xform.basis.get_rotation_quaternion(); + node->position = mi_xform.origin; - Skeleton *skeleton = Object::cast_to(mi->get_node(mi->get_skeleton_path())); + Skeleton3D *skeleton = Object::cast_to(mi->get_node(mi->get_skeleton_path())); if (!skeleton) { continue; } @@ -5988,30 +6144,30 @@ void GLTFDocument::_convert_mesh_instances(Ref state) { } Ref skin = mi->get_skin(); Ref gltf_skin; - gltf_skin.instance(); + gltf_skin.instantiate(); Array json_joints; NodePath skeleton_path = mi->get_skeleton_path(); Node *skel_node = mi->get_node_or_null(skeleton_path); - Skeleton *pandemonium_skeleton = nullptr; + Skeleton3D *godot_skeleton = nullptr; if (skel_node != nullptr) { - pandemonium_skeleton = cast_to(skel_node); + godot_skeleton = cast_to(skel_node); } - if (pandemonium_skeleton != nullptr && state->skeleton3d_to_gltf_skeleton.has(pandemonium_skeleton->get_instance_id())) { + if (godot_skeleton != nullptr && state->skeleton3d_to_gltf_skeleton.has(godot_skeleton->get_instance_id())) { // This is a skinned mesh. If the mesh has no ARRAY_WEIGHTS or ARRAY_BONES, it will be invisible. - const GLTFSkeletonIndex skeleton_gltf_i = state->skeleton3d_to_gltf_skeleton[pandemonium_skeleton->get_instance_id()]; + const GLTFSkeletonIndex skeleton_gltf_i = state->skeleton3d_to_gltf_skeleton[godot_skeleton->get_instance_id()]; Ref gltf_skeleton = state->skeletons[skeleton_gltf_i]; int bone_cnt = skeleton->get_bone_count(); ERR_FAIL_COND(bone_cnt != gltf_skeleton->joints.size()); - ObjectID gltf_skin_key = 0; + ObjectID gltf_skin_key; if (skin.is_valid()) { gltf_skin_key = skin->get_instance_id(); } - ObjectID gltf_skel_key = pandemonium_skeleton->get_instance_id(); + ObjectID gltf_skel_key = godot_skeleton->get_instance_id(); GLTFSkinIndex skin_gltf_i = -1; GLTFNodeIndex root_gltf_i = -1; - if (!gltf_skeleton->roots.empty()) { + if (!gltf_skeleton->roots.is_empty()) { root_gltf_i = gltf_skeleton->roots[0]; } if (state->skin_and_skeleton3d_to_gltf_skin.has(gltf_skin_key) && state->skin_and_skeleton3d_to_gltf_skin[gltf_skin_key].has(gltf_skel_key)) { @@ -6019,21 +6175,21 @@ void GLTFDocument::_convert_mesh_instances(Ref state) { } else { if (skin.is_null()) { // Note that gltf_skin_key should remain null, so these can share a reference. - skin = skeleton->register_skin(nullptr)->get_skin(); + skin = skeleton->create_skin_from_rest_transforms(); } - gltf_skin.instance(); - gltf_skin->pandemonium_skin = skin; + gltf_skin.instantiate(); + gltf_skin->godot_skin = skin; gltf_skin->set_name(skin->get_name()); gltf_skin->skeleton = skeleton_gltf_i; gltf_skin->skin_root = root_gltf_i; - //gltf_state->pandemonium_to_gltf_node[skel_node] + //gltf_state->godot_to_gltf_node[skel_node] HashMap bone_name_to_idx; for (int bone_i = 0; bone_i < bone_cnt; bone_i++) { bone_name_to_idx[skeleton->get_bone_name(bone_i)] = bone_i; } for (int bind_i = 0, cnt = skin->get_bind_count(); bind_i < cnt; bind_i++) { int bone_i = skin->get_bind_bone(bind_i); - Transform bind_pose = skin->get_bind_pose(bind_i); + Transform3D bind_pose = skin->get_bind_pose(bind_i); StringName bind_name = skin->get_bind_name(bind_i); if (bind_name != StringName()) { bone_i = bone_name_to_idx[bind_name]; @@ -6100,29 +6256,29 @@ void GLTFDocument::_process_mesh_instances(Ref state, Node *scene_roo if (node->skin >= 0 && node->mesh >= 0) { const GLTFSkinIndex skin_i = node->skin; - Map::Element *mi_element = state->scene_nodes.find(node_i); - ERR_CONTINUE_MSG(mi_element == nullptr, vformat("Unable to find node %d", node_i)); + HashMap::Iterator mi_element = state->scene_nodes.find(node_i); + ERR_CONTINUE_MSG(!mi_element, vformat("Unable to find node %d", node_i)); - MeshInstance *mi = Object::cast_to(mi_element->get()); - ERR_CONTINUE_MSG(mi == nullptr, vformat("Unable to cast node %d of type %s to MeshInstance", node_i, mi_element->get()->get_class_name())); + ImporterMeshInstance3D *mi = Object::cast_to(mi_element->value); + ERR_CONTINUE_MSG(mi == nullptr, vformat("Unable to cast node %d of type %s to ImporterMeshInstance3D", node_i, mi_element->value->get_class_name())); const GLTFSkeletonIndex skel_i = state->skins.write[node->skin]->skeleton; Ref gltf_skeleton = state->skeletons.write[skel_i]; - Skeleton *skeleton = gltf_skeleton->pandemonium_skeleton; + Skeleton3D *skeleton = gltf_skeleton->godot_skeleton; ERR_CONTINUE_MSG(skeleton == nullptr, vformat("Unable to find Skeleton for node %d skin %d", node_i, skin_i)); mi->get_parent()->remove_child(mi); - skeleton->add_child(mi); + skeleton->add_child(mi, true); mi->set_owner(skeleton->get_owner()); - mi->set_skin(state->skins.write[skin_i]->pandemonium_skin); + mi->set_skin(state->skins.write[skin_i]->godot_skin); mi->set_skeleton_path(mi->get_path_to(skeleton)); - mi->set_transform(Transform()); + mi->set_transform(Transform3D()); } } } -GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref state, GLTFAnimation::Track p_track, Ref p_animation, Transform p_bone_rest, int32_t p_track_i, GLTFNodeIndex p_node_i) { +GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref state, GLTFAnimation::Track p_track, Ref p_animation, 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; @@ -6135,62 +6291,55 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref state } 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 times; + Vector 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); } - 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; + if (track_type == Animation::TYPE_SCALE_3D) { 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); + Error err = p_animation->scale_track_get_key(p_track_i, key_i, &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(); + p_track.scale_track.values.write[key_i] = scale; } - } else if (path.find(":transform") != -1) { - p_track.translation_track.times = times; - p_track.translation_track.interpolation = gltf_interpolation; + } else if (track_type == Animation::TYPE_POSITION_3D) { + p_track.position_track.times = times; + p_track.position_track.values.resize(key_count); + p_track.position_track.interpolation = gltf_interpolation; + for (int32_t key_i = 0; key_i < key_count; key_i++) { + Vector3 position; + Error err = p_animation->position_track_get_key(p_track_i, key_i, &position); + ERR_CONTINUE(err != OK); + p_track.position_track.values.write[key_i] = position; + } + } else if (track_type == Animation::TYPE_ROTATION_3D) { 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(); + Quaternion rotation; + Error err = p_animation->rotation_track_get_key(p_track_i, key_i, &rotation); + ERR_CONTINUE(err != OK); + p_track.rotation_track.values.write[key_i] = rotation; } } else if (track_type == Animation::TYPE_VALUE) { - if (path.find("/rotation_quat") != -1) { + if (path.contains(":position")) { + p_track.position_track.times = times; + p_track.position_track.interpolation = gltf_interpolation; + + p_track.position_track.values.resize(key_count); + p_track.position_track.interpolation = gltf_interpolation; + + for (int32_t key_i = 0; key_i < key_count; key_i++) { + Vector3 position = p_animation->track_get_key_value(p_track_i, key_i); + p_track.position_track.values.write[key_i] = position; + } + } else if (path.contains(":rotation")) { p_track.rotation_track.times = times; p_track.rotation_track.interpolation = gltf_interpolation; @@ -6198,36 +6347,10 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref state 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; + Vector3 rotation_radian = p_animation->track_get_key_value(p_track_i, key_i); + p_track.rotation_track.values.write[key_i] = Quaternion(rotation_radian); } - } 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++) { - 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); - p_track.rotation_track.values.write[key_i] = Quat(rotation_radian); - } - } else if (path.find(":scale") != -1) { + } else if (path.contains(":scale")) { p_track.scale_track.times = times; p_track.scale_track.interpolation = gltf_interpolation; @@ -6240,10 +6363,10 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref state } } } else if (track_type == Animation::TYPE_BEZIER) { - if (path.find("/scale") != -1) { + if (path.contains("/scale")) { 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 new_times; + Vector 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; @@ -6261,57 +6384,50 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref state 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) { + if (path.contains("/scale:x")) { 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) { + } else if (path.contains("/scale:y")) { 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) { + } else if (path.contains("/scale:z")) { 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) { + } else if (path.contains("/position")) { 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 new_times; + if (!p_track.position_track.times.size()) { + Vector 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.position_track.times = new_times; + p_track.position_track.interpolation = gltf_interpolation; - p_track.translation_track.values.resize(keys); - p_track.translation_track.interpolation = gltf_interpolation; + p_track.position_track.values.resize(keys); + p_track.position_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) { + Vector3 bezier_track = p_track.position_track.values[key_i]; + if (path.contains("/position:x")) { 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) { + } else if (path.contains("/position:y")) { 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) { + } else if (path.contains("/position:z")) { 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; + p_track.position_track.values.write[key_i] = bezier_track; } } } - return p_track; } void GLTFDocument::_convert_animation(Ref state, AnimationPlayer *ap, String p_animation_track_name) { Ref animation = ap->get_animation(p_animation_track_name); Ref gltf_animation; - gltf_animation.instance(); + gltf_animation.instantiate(); 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++) { @@ -6319,89 +6435,89 @@ void GLTFDocument::_convert_animation(Ref state, AnimationPlayer *ap, continue; } String orig_track_path = animation->track_get_path(track_i); - if (String(orig_track_path).find(":translation") != -1) { - const Vector node_suffix = String(orig_track_path).split(":translation"); + if (String(orig_track_path).contains(":position")) { + const Vector node_suffix = String(orig_track_path).split(":position"); const NodePath path = node_suffix[0]; const Node *node = ap->get_parent()->get_node_or_null(path); - for (Map::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::Element *translation_track_i = gltf_animation->get_tracks().find(node_index); + for (const KeyValue &position_scene_node_i : state->scene_nodes) { + if (position_scene_node_i.value == node) { + GLTFNodeIndex node_index = position_scene_node_i.key; + HashMap::Iterator position_track_i = gltf_animation->get_tracks().find(node_index); GLTFAnimation::Track track; - if (translation_track_i) { - track = translation_track_i->get(); + if (position_track_i) { + track = position_track_i->value; } - track = _convert_animation_track(state, track, animation, Transform(), track_i, node_index); + track = _convert_animation_track(state, track, animation, track_i, node_index); gltf_animation->get_tracks().insert(node_index, track); } } - } else if (String(orig_track_path).find(":rotation_degrees") != -1) { + } else if (String(orig_track_path).contains(":rotation_degrees")) { const Vector 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::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::Element *rotation_degree_track_i = gltf_animation->get_tracks().find(node_index); + for (const KeyValue &rotation_degree_scene_node_i : state->scene_nodes) { + if (rotation_degree_scene_node_i.value == node) { + GLTFNodeIndex node_index = rotation_degree_scene_node_i.key; + HashMap::Iterator 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 = rotation_degree_track_i->value; } - track = _convert_animation_track(state, track, animation, Transform(), track_i, node_index); + track = _convert_animation_track(state, track, animation, track_i, node_index); gltf_animation->get_tracks().insert(node_index, track); } } - } else if (String(orig_track_path).find(":scale") != -1) { + } else if (String(orig_track_path).contains(":scale")) { const Vector 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::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::Element *scale_track_i = gltf_animation->get_tracks().find(node_index); + for (const KeyValue &scale_scene_node_i : state->scene_nodes) { + if (scale_scene_node_i.value == node) { + GLTFNodeIndex node_index = scale_scene_node_i.key; + HashMap::Iterator scale_track_i = gltf_animation->get_tracks().find(node_index); GLTFAnimation::Track track; if (scale_track_i) { - track = scale_track_i->get(); + track = scale_track_i->value; } - track = _convert_animation_track(state, track, animation, Transform(), track_i, node_index); + track = _convert_animation_track(state, track, animation, track_i, node_index); gltf_animation->get_tracks().insert(node_index, track); } } - } else if (String(orig_track_path).find(":transform") != -1) { + } else if (String(orig_track_path).contains(":transform")) { const Vector 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::Element *transform_track_i = state->scene_nodes.front(); transform_track_i; transform_track_i = transform_track_i->next()) { - if (transform_track_i->get() == node) { + for (const KeyValue &transform_track_i : state->scene_nodes) { + if (transform_track_i.value == 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); + track = _convert_animation_track(state, track, animation, 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 node_suffix = String(orig_track_path).split(":blend_shapes/"); + } else if (String(orig_track_path).contains(":") && animation->track_get_type(track_i) == Animation::TYPE_BLEND_SHAPE) { + const Vector node_suffix = String(orig_track_path).split(":"); const NodePath path = node_suffix[0]; const String suffix = node_suffix[1]; Node *node = ap->get_parent()->get_node_or_null(path); - MeshInstance *mi = cast_to(node); + MeshInstance3D *mi = cast_to(node); Ref mesh = mi->get_mesh(); ERR_CONTINUE(mesh.is_null()); int32_t mesh_index = -1; - for (Map::Element *mesh_track_i = state->scene_nodes.front(); mesh_track_i; mesh_track_i = mesh_track_i->next()) { - if (mesh_track_i->get() == node) { - mesh_index = mesh_track_i->key(); + for (const KeyValue &mesh_track_i : state->scene_nodes) { + if (mesh_track_i.value == node) { + mesh_index = mesh_track_i.key; } } ERR_CONTINUE(mesh_index == -1); - Map &tracks = gltf_animation->get_tracks(); + HashMap &tracks = gltf_animation->get_tracks(); GLTFAnimation::Track track = gltf_animation->get_tracks().has(mesh_index) ? gltf_animation->get_tracks()[mesh_index] : GLTFAnimation::Track(); if (!tracks.has(mesh_index)) { for (int32_t shape_i = 0; shape_i < mesh->get_blend_shape_count(); shape_i++) { String shape_name = mesh->get_blend_shape_name(shape_i); - NodePath shape_path = String(path) + ":blend_shapes/" + shape_name; - int32_t shape_track_i = animation->find_track(shape_path); + NodePath shape_path = String(path) + ":" + shape_name; + int32_t shape_track_i = animation->find_track(shape_path, Animation::TYPE_BLEND_SHAPE); if (shape_track_i == -1) { - GLTFAnimation::Channel weight; + GLTFAnimation::Channel weight; weight.interpolation = GLTFAnimation::INTERP_LINEAR; weight.times.push_back(0.0f); weight.times.push_back(0.0f); @@ -6420,7 +6536,7 @@ void GLTFDocument::_convert_animation(Ref state, AnimationPlayer *ap, gltf_interpolation = GLTFAnimation::INTERP_CUBIC_SPLINE; } int32_t key_count = animation->track_get_key_count(shape_track_i); - GLTFAnimation::Channel weight; + GLTFAnimation::Channel weight; weight.interpolation = gltf_interpolation; weight.times.resize(key_count); for (int32_t time_i = 0; time_i < key_count; time_i++) { @@ -6434,54 +6550,50 @@ void GLTFDocument::_convert_animation(Ref state, AnimationPlayer *ap, } tracks[mesh_index] = track; } - } else if (String(orig_track_path).find(":") != -1) { + } else if (String(orig_track_path).contains(":")) { //Process skeleton const Vector 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 *pandemonium_node = ap->get_parent()->get_node_or_null(node_path); - Skeleton *skeleton = nullptr; + 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]->pandemonium_skeleton == cast_to(pandemonium_node)) { - skeleton = state->skeletons[skeleton_i]->pandemonium_skeleton; + if (state->skeletons[skeleton_i]->godot_skeleton == cast_to(godot_node)) { + skeleton = state->skeletons[skeleton_i]->godot_skeleton; skeleton_gltf_i = skeleton_i; ERR_CONTINUE(!skeleton); Ref 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->pandemonium_bone_node.has(bone)) { + if (!skeleton_gltf->godot_bone_node.has(bone)) { continue; } - GLTFNodeIndex node_i = skeleton_gltf->pandemonium_bone_node[bone]; - Map::Element *property_track_i = gltf_animation->get_tracks().find(node_i); + GLTFNodeIndex node_i = skeleton_gltf->godot_bone_node[bone]; + HashMap::Iterator property_track_i = gltf_animation->get_tracks().find(node_i); GLTFAnimation::Track track; if (property_track_i) { - track = property_track_i->get(); + track = property_track_i->value; } - track = _convert_animation_track(state, track, animation, xform, track_i, node_i); + track = _convert_animation_track(state, track, animation, track_i, node_i); gltf_animation->get_tracks()[node_i] = track; } } - } else if (String(orig_track_path).find(":") == -1) { + } else if (!String(orig_track_path).contains(":")) { ERR_CONTINUE(!ap->get_parent()); - for (int32_t node_i = 0; node_i < ap->get_parent()->get_child_count(); node_i++) { - const Node *child = ap->get_parent()->get_child(node_i); - const Node *node = child->get_node_or_null(orig_track_path); - for (Map::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::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; + Node *godot_node = ap->get_parent()->get_node_or_null(orig_track_path); + for (const KeyValue &scene_node_i : state->scene_nodes) { + if (scene_node_i.value == godot_node) { + GLTFNodeIndex node_i = scene_node_i.key; + HashMap::Iterator node_track_i = gltf_animation->get_tracks().find(node_i); + GLTFAnimation::Track track; + if (node_track_i) { + track = node_track_i->value; } + track = _convert_animation_track(state, track, animation, track_i, node_i); + gltf_animation->get_tracks()[node_i] = track; + break; } } } @@ -6491,191 +6603,101 @@ void GLTFDocument::_convert_animation(Ref state, AnimationPlayer *ap, } } -Error GLTFDocument::parse(Ref state, String p_path, bool p_read_binary) { +Error GLTFDocument::_parse(Ref state, String p_path, Ref f, int p_bake_fps) { Error err; - FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err); - if (!f) { - return err; + if (f.is_null()) { + return FAILED; } + f->seek(0); uint32_t magic = f->get_32(); if (magic == 0x46546C67) { //binary file //text file - err = _parse_glb(p_path, state); - if (err) { - return FAILED; + f->seek(0); + err = _parse_glb(f, state); + if (err != OK) { + return err; } } else { - //text file - err = _parse_json(p_path, state); - if (err) { - return FAILED; + f->seek(0); + String text = f->get_as_utf8_string(); + JSON json; + err = json.parse(text); + if (err != OK) { + _err_print_error("", "", json.get_error_line(), json.get_error_message().utf8().get_data(), false, ERR_HANDLER_SCRIPT); } + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + state->json = json.get_data(); } - f->close(); - // get file's name, use for scene name if none - state->filename = p_path.get_file().get_slice(".", 0); - - ERR_FAIL_COND_V(!state->json.has("asset"), Error::FAILED); + if (!state->json.has("asset")) { + return ERR_PARSE_ERROR; + } Dictionary asset = state->json["asset"]; - ERR_FAIL_COND_V(!asset.has("version"), Error::FAILED); + if (!asset.has("version")) { + return ERR_PARSE_ERROR; + } String version = asset["version"]; state->major_version = version.get_slice(".", 0).to_int(); state->minor_version = version.get_slice(".", 1).to_int(); - /* PARSE EXTENSIONS */ - - err = _parse_gltf_extensions(state); - if (err != OK) { - return Error::FAILED; + for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { + Ref ext = document_extensions[ext_i]; + ERR_CONTINUE(ext.is_null()); + err = ext->import_preflight(state); + ERR_FAIL_COND_V(err != OK, err); } - - /* PARSE SCENE */ - err = _parse_scenes(state); - if (err != OK) { - return Error::FAILED; - } - - /* PARSE NODES */ - err = _parse_nodes(state); - if (err != OK) { - return Error::FAILED; - } - - /* PARSE BUFFERS */ - err = _parse_buffers(state, p_path.get_base_dir()); - if (err != OK) { - return Error::FAILED; - } - - /* PARSE BUFFER VIEWS */ - err = _parse_buffer_views(state); - if (err != OK) { - return Error::FAILED; - } - - /* PARSE ACCESSORS */ - err = _parse_accessors(state); - if (err != OK) { - return Error::FAILED; - } - - /* PARSE IMAGES */ - err = _parse_images(state, p_path.get_base_dir()); - if (err != OK) { - return Error::FAILED; - } - - /* PARSE TEXTURES */ - err = _parse_textures(state); - if (err != OK) { - return Error::FAILED; - } - - /* PARSE TEXTURES */ - err = _parse_materials(state); - if (err != OK) { - return Error::FAILED; - } - - /* PARSE SKINS */ - err = _parse_skins(state); - if (err != OK) { - return Error::FAILED; - } - - /* DETERMINE SKELETONS */ - err = _determine_skeletons(state); - if (err != OK) { - return Error::FAILED; - } - - /* CREATE SKELETONS */ - err = _create_skeletons(state); - if (err != OK) { - return Error::FAILED; - } - - /* CREATE SKINS */ - err = _create_skins(state); - if (err != OK) { - return Error::FAILED; - } - - /* PARSE MESHES (we have enough info now) */ - err = _parse_meshes(state); - if (err != OK) { - return Error::FAILED; - } - - /* PARSE LIGHTS */ - err = _parse_lights(state); - if (err != OK) { - return Error::FAILED; - } - - /* PARSE CAMERAS */ - err = _parse_cameras(state); - if (err != OK) { - return Error::FAILED; - } - - /* PARSE ANIMATIONS */ - err = _parse_animations(state); - if (err != OK) { - return Error::FAILED; - } - - /* ASSIGN SCENE NAMES */ - _assign_scene_names(state); - + err = _parse_gltf_state(state, p_path, p_bake_fps); + ERR_FAIL_COND_V(err != OK, err); return OK; } -Dictionary GLTFDocument::_serialize_texture_transform_uv2(Ref p_material) { - Dictionary extension; - Ref mat = p_material; - if (mat.is_valid()) { - Dictionary texture_transform; +Dictionary _serialize_texture_transform_uv(Vector2 p_offset, Vector2 p_scale) { + Dictionary texture_transform; + bool is_offset = p_offset != Vector2(0.0, 0.0); + if (is_offset) { Array offset; offset.resize(2); - offset[0] = mat->get_uv2_offset().x; - offset[1] = mat->get_uv2_offset().y; + offset[0] = p_offset.x; + offset[1] = p_offset.y; texture_transform["offset"] = offset; + } + bool is_scaled = p_scale != Vector2(1.0, 1.0); + if (is_scaled) { Array scale; scale.resize(2); - scale[0] = mat->get_uv2_scale().x; - scale[1] = mat->get_uv2_scale().y; + scale[0] = p_scale.x; + scale[1] = p_scale.y; texture_transform["scale"] = scale; - // Pandemonium doesn't support texture rotation + } + Dictionary extension; + // Note: Godot doesn't support texture rotation. + if (is_offset || is_scaled) { extension["KHR_texture_transform"] = texture_transform; } return extension; } -Dictionary GLTFDocument::_serialize_texture_transform_uv1(Ref p_material) { - Dictionary extension; +Dictionary GLTFDocument::_serialize_texture_transform_uv1(Ref p_material) { 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; - // Pandemonium doesn't support texture rotation - extension["KHR_texture_transform"] = texture_transform; + Vector3 offset = p_material->get_uv1_offset(); + Vector3 scale = p_material->get_uv1_scale(); + return _serialize_texture_transform_uv(Vector2(offset.x, offset.y), Vector2(scale.x, scale.y)); } - return extension; + return Dictionary(); +} + +Dictionary GLTFDocument::_serialize_texture_transform_uv2(Ref p_material) { + if (p_material.is_valid()) { + Vector3 offset = p_material->get_uv2_offset(); + Vector3 scale = p_material->get_uv2_scale(); + return _serialize_texture_transform_uv(Vector2(offset.x, offset.y), Vector2(scale.x, scale.y)); + } + return Dictionary(); } Error GLTFDocument::_serialize_version(Ref state) { @@ -6686,7 +6708,7 @@ Error GLTFDocument::_serialize_version(Ref state) { asset["version"] = version; String hash = String(VERSION_HASH); - asset["generator"] = String(VERSION_FULL_NAME) + String("@") + (hash.empty() ? String("unknown") : hash); + asset["generator"] = String(VERSION_FULL_NAME) + String("@") + (hash.is_empty() ? 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); @@ -6698,10 +6720,10 @@ Error GLTFDocument::_serialize_file(Ref state, const String p_path) { 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); + Ref f = FileAccess::open(p_path, FileAccess::WRITE, &err); + ERR_FAIL_COND_V(f.is_null(), FAILED); - String json = JSON::print(state->json); + String json = Variant(state->json).to_json_string(); const uint32_t magic = 0x46546C67; // GLTF const int32_t header_size = 12; @@ -6736,24 +6758,341 @@ Error GLTFDocument::_serialize_file(Ref state, const String p_path) { for (uint32_t pad_i = binary_data_length; pad_i < binary_chunk_length; pad_i++) { f->store_8(0); } - - 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); + Ref f = FileAccess::open(p_path, FileAccess::WRITE, &err); + ERR_FAIL_COND_V(f.is_null(), FAILED); f->create(FileAccess::ACCESS_RESOURCES); - String json = JSON::print(state->json); + String json = Variant(state->json).to_json_string(); f->store_string(json); - f->close(); } return err; } +void GLTFDocument::_bind_methods() { + ClassDB::bind_method(D_METHOD("append_from_file", "path", "state", "flags", "bake_fps", "base_path"), + &GLTFDocument::append_from_file, DEFVAL(0), DEFVAL(30), DEFVAL(String())); + ClassDB::bind_method(D_METHOD("append_from_buffer", "bytes", "base_path", "state", "flags", "bake_fps"), + &GLTFDocument::append_from_buffer, DEFVAL(0), DEFVAL(30)); + ClassDB::bind_method(D_METHOD("append_from_scene", "node", "state", "flags", "bake_fps"), + &GLTFDocument::append_from_scene, DEFVAL(0), DEFVAL(30)); + ClassDB::bind_method(D_METHOD("generate_scene", "state", "bake_fps"), + &GLTFDocument::generate_scene, DEFVAL(30)); + ClassDB::bind_method(D_METHOD("generate_buffer", "state"), + &GLTFDocument::generate_buffer); + ClassDB::bind_method(D_METHOD("write_to_filesystem", "state", "path"), + &GLTFDocument::write_to_filesystem); + + ClassDB::bind_method(D_METHOD("set_extensions", "extensions"), + &GLTFDocument::set_extensions); + ClassDB::bind_method(D_METHOD("get_extensions"), + &GLTFDocument::get_extensions); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "extensions", PROPERTY_HINT_ARRAY_TYPE, + vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "GLTFDocumentExtension"), + PROPERTY_USAGE_DEFAULT), + "set_extensions", "get_extensions"); +} + +void GLTFDocument::_build_parent_hierachy(Ref 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; + } + } +} + +void GLTFDocument::set_extensions(TypedArray p_extensions) { + document_extensions = p_extensions; +} + +TypedArray GLTFDocument::get_extensions() const { + return document_extensions; +} + +GLTFDocument::GLTFDocument() { + bool is_editor = ::Engine::get_singleton()->is_editor_hint(); + if (is_editor) { + return; + } + Ref extension_editor; + extension_editor.instantiate(); + document_extensions.push_back(extension_editor); +} + +PackedByteArray GLTFDocument::_serialize_glb_buffer(Ref state, Error *r_err) { + Error err = _encode_buffer_glb(state, ""); + if (r_err) { + *r_err = err; + } + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + String json = Variant(state->json).to_json_string(); + + 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 + + Ref buffer; + buffer.instantiate(); + buffer->put_32(magic); + buffer->put_32(state->major_version); // version + buffer->put_32(header_size + chunk_header_size + text_chunk_length + chunk_header_size + binary_data_length); // length + buffer->put_32(text_chunk_length); + buffer->put_32(text_chunk_type); + buffer->put_data((uint8_t *)&cs[0], cs.length()); + if (binary_chunk_length) { + buffer->put_32(binary_chunk_length); + buffer->put_32(binary_chunk_type); + buffer->put_data(state->buffers[0].ptr(), binary_data_length); + } + return buffer->get_data_array(); +} + +PackedByteArray GLTFDocument::generate_buffer(Ref state) { + ERR_FAIL_NULL_V(state, PackedByteArray()); + Error err = _serialize(state, ""); + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + PackedByteArray bytes = _serialize_glb_buffer(state, &err); + return bytes; +} + +Error GLTFDocument::write_to_filesystem(Ref state, const String &p_path) { + ERR_FAIL_NULL_V(state, ERR_INVALID_PARAMETER); + Error err = _serialize(state, p_path); + if (err != OK) { + return err; + } + err = _serialize_file(state, p_path); + if (err != OK) { + return Error::FAILED; + } + return OK; +} + +Node *GLTFDocument::generate_scene(Ref state, int32_t p_bake_fps) { + ERR_FAIL_NULL_V(state, nullptr); + ERR_FAIL_INDEX_V(0, state->root_nodes.size(), nullptr); + Error err = OK; + GLTFNodeIndex gltf_root = state->root_nodes.write[0]; + Node *gltf_root_node = state->get_scene_node(gltf_root); + Node *root = gltf_root_node->get_parent(); + ERR_FAIL_NULL_V(root, nullptr); + _process_mesh_instances(state, root); + if (state->animations.size()) { + AnimationPlayer *ap = memnew(AnimationPlayer); + root->add_child(ap, true); + ap->set_owner(root); + for (int i = 0; i < state->animations.size(); i++) { + _import_animation(state, ap, i, p_bake_fps); + } + } + for (KeyValue E : state->scene_nodes) { + ERR_CONTINUE(!E.value); + for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { + Ref ext = document_extensions[ext_i]; + ERR_CONTINUE(ext.is_null()); + ERR_CONTINUE(!state->json.has("nodes")); + Array nodes = state->json["nodes"]; + ERR_CONTINUE(E.key >= nodes.size()); + ERR_CONTINUE(E.key < 0); + Dictionary node_json = nodes[E.key]; + Ref gltf_node = state->nodes[E.key]; + err = ext->import_node(state, gltf_node, node_json, E.value); + ERR_CONTINUE(err != OK); + } + } + for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { + Ref ext = document_extensions[ext_i]; + ERR_CONTINUE(ext.is_null()); + err = ext->import_post(state, root); + ERR_CONTINUE(err != OK); + } + ERR_FAIL_NULL_V(root, nullptr); + return root; +} + +Error GLTFDocument::append_from_scene(Node *p_node, Ref state, uint32_t p_flags, int32_t p_bake_fps) { + ERR_FAIL_COND_V(state.is_null(), FAILED); + state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS; + state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS; + + for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { + Ref ext = document_extensions[ext_i]; + ERR_CONTINUE(ext.is_null()); + Error err = ext->export_preflight(p_node); + ERR_FAIL_COND_V(err != OK, FAILED); + } + _convert_scene_node(state, p_node, -1, -1); + if (!state->buffers.size()) { + state->buffers.push_back(Vector()); + } + return OK; +} + +Error GLTFDocument::append_from_buffer(PackedByteArray p_bytes, String p_base_path, Ref state, uint32_t p_flags, int32_t p_bake_fps) { + ERR_FAIL_COND_V(state.is_null(), FAILED); + // TODO Add missing texture and missing .bin file paths to r_missing_deps 2021-09-10 fire + Error err = FAILED; + state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS; + state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS; + + Ref file_access; + file_access.instantiate(); + file_access->open_custom(p_bytes.ptr(), p_bytes.size()); + state->base_path = p_base_path.get_base_dir(); + err = _parse(state, state->base_path, file_access, p_bake_fps); + ERR_FAIL_COND_V(err != OK, err); + for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { + Ref ext = document_extensions[ext_i]; + ERR_CONTINUE(ext.is_null()); + err = ext->import_post_parse(state); + ERR_FAIL_COND_V(err != OK, err); + } + return OK; +} + +Error GLTFDocument::_parse_gltf_state(Ref state, const String &p_search_path, float p_bake_fps) { + Error err; + + /* PARSE EXTENSIONS */ + err = _parse_gltf_extensions(state); + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* PARSE SCENE */ + err = _parse_scenes(state); + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* PARSE NODES */ + err = _parse_nodes(state); + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* PARSE BUFFERS */ + err = _parse_buffers(state, p_search_path); + + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* PARSE BUFFER VIEWS */ + err = _parse_buffer_views(state); + + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* PARSE ACCESSORS */ + err = _parse_accessors(state); + + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + if (!state->discard_meshes_and_materials) { + /* PARSE IMAGES */ + err = _parse_images(state, p_search_path); + + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* PARSE TEXTURES */ + err = _parse_textures(state); + + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* PARSE TEXTURES */ + err = _parse_materials(state); + + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + } + + /* PARSE SKINS */ + err = _parse_skins(state); + + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* DETERMINE SKELETONS */ + err = _determine_skeletons(state); + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* CREATE SKELETONS */ + err = _create_skeletons(state); + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* CREATE SKINS */ + err = _create_skins(state); + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* PARSE MESHES (we have enough info now) */ + err = _parse_meshes(state); + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* PARSE LIGHTS */ + err = _parse_lights(state); + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* PARSE CAMERAS */ + err = _parse_cameras(state); + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* PARSE ANIMATIONS */ + err = _parse_animations(state); + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + + /* ASSIGN SCENE NAMES */ + _assign_scene_names(state); + + Node3D *root = memnew(Node3D); + for (int32_t root_i = 0; root_i < state->root_nodes.size(); root_i++) { + _generate_scene_node(state, root, root, state->root_nodes[root_i]); + } + + return OK; +} + +Error GLTFDocument::append_from_file(String p_path, Ref r_state, uint32_t p_flags, int32_t p_bake_fps, String p_base_path) { + // TODO Add missing texture and missing .bin file paths to r_missing_deps 2021-09-10 fire + if (r_state == Ref()) { + r_state.instantiate(); + } + r_state->filename = p_path.get_file().get_basename(); + r_state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS; + r_state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS; + Error err; + Ref f = FileAccess::open(p_path, FileAccess::READ, &err); + ERR_FAIL_COND_V(err != OK, ERR_FILE_CANT_OPEN); + ERR_FAIL_NULL_V(f, ERR_FILE_CANT_OPEN); + String base_path = p_base_path; + if (base_path.is_empty()) { + base_path = p_path.get_base_dir(); + } + r_state->base_path = base_path; + err = _parse(r_state, base_path, f, p_bake_fps); + ERR_FAIL_COND_V(err != OK, err); + for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { + Ref ext = document_extensions[ext_i]; + ERR_CONTINUE(ext.is_null()); + err = ext->import_post_parse(r_state); + ERR_FAIL_COND_V(err != OK, err); + } + return OK; +} + Error GLTFDocument::_parse_gltf_extensions(Ref state) { - ERR_FAIL_COND_V(!state.is_valid(), ERR_PARSE_ERROR); + ERR_FAIL_NULL_V(state, ERR_PARSE_ERROR); if (state->json.has("extensionsRequired") && state->json["extensionsRequired"].get_type() == Variant::ARRAY) { Array extensions_required = state->json["extensionsRequired"]; if (extensions_required.find("KHR_draco_mesh_compression") != -1) { diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h index 07e0ff2b7..36a2f94a4 100644 --- a/modules/gltf/gltf_document.h +++ b/modules/gltf/gltf_document.h @@ -1,5 +1,3 @@ -#ifndef GLTF_DOCUMENT_H -#define GLTF_DOCUMENT_H /*************************************************************************/ /* gltf_document.h */ /*************************************************************************/ @@ -30,61 +28,30 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "scene/3d/bone_attachment.h" -#include "scene/3d/camera.h" -#include "scene/3d/light.h" -#include "scene/3d/mesh_instance.h" -#include "scene/3d/skeleton.h" -#include "scene/3d/spatial.h" +#ifndef GLTF_DOCUMENT_H +#define GLTF_DOCUMENT_H + +#include "gltf_defines.h" +#include "structures/gltf_animation.h" + +#include "scene/3d/bone_attachment_3d.h" +#include "scene/3d/importer_mesh_instance_3d.h" +#include "scene/3d/mesh_instance_3d.h" #include "scene/animation/animation_player.h" #include "scene/resources/material.h" -#include "scene/resources/texture.h" - -#include "gltf_animation.h" #include "modules/modules_enabled.gen.h" // For csg, gridmap. -class GLTFState; -class GLTFSkin; -class GLTFNode; -class GLTFSpecGloss; -class GLTFSkeleton; -class MultiMeshInstance; - -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; + TypedArray document_extensions; private: const float BAKE_FPS = 30.0f; public: + GLTFDocument(); 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, @@ -105,78 +72,34 @@ public: COMPONENT_TYPE_FLOAT = 5126, }; +protected: + static void _bind_methods(); + +public: + void set_extensions(TypedArray p_extensions); + TypedArray get_extensions() const; + private: - template - static Array to_array(const Vector &p_inp) { - Array ret; - for (int i = 0; i < p_inp.size(); i++) { - ret.push_back(p_inp[i]); - } - return ret; - } - - template - static Array to_array(const Set &p_inp) { - Array ret; - typename Set::Element *elem = p_inp.front(); - while (elem) { - ret.push_back(elem->get()); - elem = elem->next(); - } - return ret; - } - - template - static void set_from_array(Vector &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 - static void set_from_array(Set &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 - static Dictionary to_dict(const Map &p_inp) { - Dictionary ret; - for (typename Map::Element *E = p_inp.front(); E; E = E->next()) { - ret[E->key()] = E->value(); - } - return ret; - } - - template - static void set_from_dict(Map &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]]; - } - } + void _build_parent_hierachy(Ref state); 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 state); Error _parse_nodes(Ref state); String _get_type_name(const GLTFType p_component); - String _get_accessor_type_name(const GLTFDocument::GLTFType p_type); + String _get_accessor_type_name(const GLTFType p_type); String _gen_unique_name(Ref state, const String &p_name); String _sanitize_animation_name(const String &name); String _gen_unique_animation_name(Ref state, const String &p_name); - String _sanitize_bone_name(Ref state, const String &name); + String _sanitize_bone_name(const String &name); String _gen_unique_bone_name(Ref state, const GLTFSkeletonIndex skel_i, const String &p_name); - GLTFTextureIndex _set_texture(Ref state, Ref p_texture); - Ref _get_texture(Ref state, + GLTFTextureIndex _set_texture(Ref state, Ref p_texture); + Ref _get_texture(Ref state, const GLTFTextureIndex p_texture); Error _parse_json(const String &p_path, Ref state); - Error _parse_glb(const String &p_path, Ref state); + Error _parse_glb(Ref f, Ref state); void _compute_node_heights(Ref state); Error _parse_buffers(Ref state, const String &p_base_path); Error _parse_buffer_views(Ref state); @@ -208,7 +131,7 @@ private: Vector _decode_accessor_as_color(Ref state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex); - Vector _decode_accessor_as_quat(Ref state, + Vector _decode_accessor_as_quaternion(Ref state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex); Vector _decode_accessor_as_xform2d(Ref state, @@ -217,7 +140,7 @@ private: Vector _decode_accessor_as_basis(Ref state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex); - Vector _decode_accessor_as_xform(Ref state, + Vector _decode_accessor_as_xform(Ref state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex); Error _parse_meshes(Ref state); @@ -227,9 +150,9 @@ private: Error _parse_images(Ref state, const String &p_base_path); Error _parse_textures(Ref state); Error _parse_materials(Ref state); - void _set_texture_transform_uv1(const Dictionary &d, Ref material); + void _set_texture_transform_uv1(const Dictionary &d, Ref material); void spec_gloss_to_rough_metal(Ref r_spec_gloss, - Ref p_material); + Ref p_material); static void spec_gloss_to_metal_base_color(const Color &p_specular_factor, const Color &p_diffuse, Color &r_base_color, @@ -259,23 +182,21 @@ private: Error _parse_lights(Ref state); Error _parse_animations(Ref state); Error _serialize_animations(Ref state); - BoneAttachment *_generate_bone_attachment(Ref state, - Skeleton *skeleton, + BoneAttachment3D *_generate_bone_attachment(Ref state, + Skeleton3D *skeleton, const GLTFNodeIndex node_index, const GLTFNodeIndex bone_index); - Spatial *_generate_mesh_instance(Ref state, Node *scene_parent, const GLTFNodeIndex node_index); - Camera *_generate_camera(Ref state, Node *scene_parent, - const GLTFNodeIndex node_index); - Spatial *_generate_light(Ref state, Node *scene_parent, const GLTFNodeIndex node_index); - Spatial *_generate_spatial(Ref state, Node *scene_parent, - const GLTFNodeIndex node_index); + ImporterMeshInstance3D *_generate_mesh_instance(Ref state, const GLTFNodeIndex node_index); + Camera3D *_generate_camera(Ref state, const GLTFNodeIndex node_index); + Node3D *_generate_light(Ref state, const GLTFNodeIndex node_index); + Node3D *_generate_spatial(Ref state, const GLTFNodeIndex node_index); void _assign_scene_names(Ref state); template - T _interpolate_track(const Vector &p_times, const Vector &p_values, + T _interpolate_track(const Vector &p_times, const Vector &p_values, const float p_time, const GLTFAnimation::Interpolation p_interp); - GLTFAccessorIndex _encode_accessor_as_quats(Ref state, - const Vector p_attribs, + GLTFAccessorIndex _encode_accessor_as_quaternions(Ref state, + const Vector p_attribs, const bool p_for_vertex); GLTFAccessorIndex _encode_accessor_as_weights(Ref state, const Vector p_attribs, @@ -318,7 +239,7 @@ private: const Vector p_attribs, const bool p_for_vertex); GLTFAccessorIndex _encode_accessor_as_xform(Ref state, - const Vector p_attribs, + const Vector p_attribs, const bool p_for_vertex); Error _encode_buffer_view(Ref state, const double *src, const int count, const GLTFType type, @@ -334,20 +255,21 @@ private: String interpolation_to_string(const GLTFAnimation::Interpolation p_interp); GLTFAnimation::Track _convert_animation_track(Ref state, GLTFAnimation::Track p_track, - Ref p_animation, Transform p_bone_rest, + Ref p_animation, int32_t p_track_i, GLTFNodeIndex p_node_i); Error _encode_buffer_bins(Ref state, const String &p_path); Error _encode_buffer_glb(Ref state, const String &p_path); - Dictionary _serialize_texture_transform_uv1(Ref p_material); - Dictionary _serialize_texture_transform_uv2(Ref p_material); + PackedByteArray _serialize_glb_buffer(Ref state, Error *r_err); + Dictionary _serialize_texture_transform_uv1(Ref p_material); + Dictionary _serialize_texture_transform_uv2(Ref p_material); Error _serialize_version(Ref state); Error _serialize_file(Ref state, const String p_path); Error _serialize_extensions(Ref 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 + // https://www.itu.int/rec/R-REC-BT.601 + // https://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; @@ -362,27 +284,38 @@ private: static float get_max_component(const Color &p_color); public: - String _sanitize_scene_name(Ref state, const String &p_name); - String _legacy_validate_node_name(const String &p_name); + Error append_from_file(String p_path, Ref r_state, uint32_t p_flags = 0, int32_t p_bake_fps = 30, String p_base_path = String()); + Error append_from_buffer(PackedByteArray p_bytes, String p_base_path, Ref r_state, uint32_t p_flags = 0, int32_t p_bake_fps = 30); + Error append_from_scene(Node *p_node, Ref r_state, uint32_t p_flags = 0, int32_t p_bake_fps = 30); +public: + Node *generate_scene(Ref state, int32_t p_bake_fps = 30.0f); + PackedByteArray generate_buffer(Ref state); + Error write_to_filesystem(Ref state, const String &p_path); + +public: + Error _parse_gltf_state(Ref state, const String &p_search_path, float p_bake_fps); Error _parse_gltf_extensions(Ref state); - void _process_mesh_instances(Ref state, Node *scene_root); void _generate_scene_node(Ref state, Node *scene_parent, - Spatial *scene_root, + Node3D *scene_root, const GLTFNodeIndex node_index); - void _generate_skeleton_bone_node(Ref state, Node *scene_parent, Spatial *scene_root, const GLTFNodeIndex node_index); + void _generate_skeleton_bone_node(Ref state, Node *scene_parent, Node3D *scene_root, const GLTFNodeIndex node_index); void _import_animation(Ref state, AnimationPlayer *ap, const GLTFAnimationIndex index, const int bake_fps); void _convert_mesh_instances(Ref state); - GLTFCameraIndex _convert_camera(Ref state, Camera *p_camera); - void _convert_light_to_gltf(Light *light, Ref state, Ref gltf_node); - GLTFLightIndex _convert_light(Ref state, Light *p_light); - void _convert_spatial(Ref state, Spatial *p_spatial, Ref p_node); + GLTFCameraIndex _convert_camera(Ref state, Camera3D *p_camera); + void _convert_light_to_gltf(Light3D *light, Ref state, Ref gltf_node); + GLTFLightIndex _convert_light(Ref state, Light3D *p_light); + void _convert_spatial(Ref state, Node3D *p_spatial, Ref p_node); void _convert_scene_node(Ref state, Node *p_current, const GLTFNodeIndex p_gltf_current, const GLTFNodeIndex p_gltf_root); +#ifdef MODULE_CSG_ENABLED + void _convert_csg_shape_to_gltf(CSGShape3D *p_current, GLTFNodeIndex p_gltf_parent, Ref gltf_node, Ref state); +#endif // MODULE_CSG_ENABLED + void _create_gltf_node(Ref state, Node *p_scene_parent, GLTFNodeIndex current_node_i, @@ -395,32 +328,39 @@ public: GLTFNodeIndex p_gltf_root_index, Ref p_gltf_node, Node *p_scene_parent); void _check_visibility(Node *p_node, bool &retflag); - void _convert_camera_to_gltf(Camera *camera, Ref state, + void _convert_camera_to_gltf(Camera3D *camera, Ref state, Ref gltf_node); - void _convert_mult_mesh_instance_to_gltf( - MultiMeshInstance *p_scene_parent, +#ifdef MODULE_GRIDMAP_ENABLED + void _convert_grid_map_to_gltf( + GridMap *p_grid_map, + GLTFNodeIndex p_parent_node_index, + GLTFNodeIndex p_root_node_index, + Ref gltf_node, Ref state); +#endif // MODULE_GRIDMAP_ENABLED + void _convert_multi_mesh_instance_to_gltf( + MultiMeshInstance3D *p_multi_mesh_instance, GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_node_index, Ref gltf_node, Ref state); void _convert_skeleton_to_gltf( - Skeleton *p_scene_parent, Ref state, + Skeleton3D *p_scene_parent, Ref state, GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_node_index, Ref gltf_node); - void _convert_bone_attachment_to_gltf(BoneAttachment *p_bone_attachment, + void _convert_bone_attachment_to_gltf(BoneAttachment3D *p_bone_attachment, Ref state, GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_node_index, Ref gltf_node); - void _convert_mesh_instance_to_gltf(MeshInstance *p_mesh_instance, + void _convert_mesh_instance_to_gltf(MeshInstance3D *p_mesh_instance, Ref state, Ref gltf_node); GLTFMeshIndex _convert_mesh_to_gltf(Ref state, - MeshInstance *p_mesh_instance); + MeshInstance3D *p_mesh_instance); void _convert_animation(Ref state, AnimationPlayer *ap, String p_animation_track_name); - Error serialize(Ref state, Node *p_root, const String &p_path); - Error parse(Ref state, String p_paths, bool p_read_binary = false); + Error _serialize(Ref state, const String &p_path); + Error _parse(Ref state, String p_path, Ref f, int p_bake_fps); }; #endif // GLTF_DOCUMENT_H diff --git a/modules/gltf/gltf_document_extension.cpp b/modules/gltf/gltf_document_extension.cpp new file mode 100644 index 000000000..d0bd7651e --- /dev/null +++ b/modules/gltf/gltf_document_extension.cpp @@ -0,0 +1,108 @@ +/*************************************************************************/ +/* gltf_document_extension.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (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_extension.h" + +void GLTFDocumentExtension::_bind_methods() { + GDVIRTUAL_BIND(_import_preflight, "state"); + GDVIRTUAL_BIND(_import_post_parse, "state"); + GDVIRTUAL_BIND(_import_node, "state", "gltf_node", "json", "node"); + GDVIRTUAL_BIND(_import_post, "state", "root"); + GDVIRTUAL_BIND(_export_preflight, "root"); + GDVIRTUAL_BIND(_export_node, "state", "gltf_node", "json", "node"); + GDVIRTUAL_BIND(_export_post, "state"); +} + +Error GLTFDocumentExtension::import_post(Ref p_state, Node *p_root) { + ERR_FAIL_NULL_V(p_root, ERR_INVALID_PARAMETER); + ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); + int err = OK; + if (GDVIRTUAL_CALL(_import_post, p_state, p_root, err)) { + return Error(err); + } + return OK; +} + +Error GLTFDocumentExtension::import_preflight(Ref p_state) { + ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); + int err = OK; + if (GDVIRTUAL_CALL(_import_preflight, p_state, err)) { + return Error(err); + } + return OK; +} + +Error GLTFDocumentExtension::import_post_parse(Ref p_state) { + ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); + int err = OK; + if (GDVIRTUAL_CALL(_import_post_parse, p_state, err)) { + return Error(err); + } + return OK; +} + +Error GLTFDocumentExtension::export_post(Ref p_state) { + ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); + int err = OK; + if (GDVIRTUAL_CALL(_export_post, p_state, err)) { + return Error(err); + } + return OK; +} +Error GLTFDocumentExtension::export_preflight(Node *p_root) { + ERR_FAIL_NULL_V(p_root, ERR_INVALID_PARAMETER); + int err = OK; + if (GDVIRTUAL_CALL(_export_preflight, p_root, err)) { + return Error(err); + } + return OK; +} + +Error GLTFDocumentExtension::import_node(Ref p_state, Ref p_gltf_node, Dictionary &r_dict, Node *p_node) { + ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); + ERR_FAIL_NULL_V(p_gltf_node, ERR_INVALID_PARAMETER); + ERR_FAIL_NULL_V(p_node, ERR_INVALID_PARAMETER); + int err = OK; + if (GDVIRTUAL_CALL(_import_node, p_state, p_gltf_node, r_dict, p_node, err)) { + return Error(err); + } + return OK; +} + +Error GLTFDocumentExtension::export_node(Ref p_state, Ref p_gltf_node, Dictionary &r_dict, Node *p_node) { + ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); + ERR_FAIL_NULL_V(p_gltf_node, ERR_INVALID_PARAMETER); + ERR_FAIL_NULL_V(p_node, ERR_INVALID_PARAMETER); + int err = OK; + if (GDVIRTUAL_CALL(_export_node, p_state, p_gltf_node, r_dict, p_node, err)) { + return Error(err); + } + return OK; +} diff --git a/modules/gltf/gltf_document_extension.h b/modules/gltf/gltf_document_extension.h new file mode 100644 index 000000000..0ef910958 --- /dev/null +++ b/modules/gltf/gltf_document_extension.h @@ -0,0 +1,60 @@ +/*************************************************************************/ +/* gltf_document_extension.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (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_EXTENSION_H +#define GLTF_DOCUMENT_EXTENSION_H + +#include "gltf_state.h" +#include "structures/gltf_node.h" + +class GLTFDocumentExtension : public Resource { + GDCLASS(GLTFDocumentExtension, Resource); + +protected: + static void _bind_methods(); + +public: + virtual Error import_preflight(Ref p_state); + virtual Error import_post_parse(Ref p_state); + virtual Error export_post(Ref p_state); + virtual Error import_post(Ref p_state, Node *p_node); + virtual Error export_preflight(Node *p_state); + virtual Error import_node(Ref p_state, Ref p_gltf_node, Dictionary &r_json, Node *p_node); + virtual Error export_node(Ref p_state, Ref p_gltf_node, Dictionary &r_json, Node *p_node); + GDVIRTUAL1R(int, _import_preflight, Ref); + GDVIRTUAL1R(int, _import_post_parse, Ref); + GDVIRTUAL4R(int, _import_node, Ref, Ref, Dictionary, Node *); + GDVIRTUAL2R(int, _import_post, Ref, Node *); + GDVIRTUAL1R(int, _export_preflight, Node *); + GDVIRTUAL4R(int, _export_node, Ref, Ref, Dictionary, Node *); + GDVIRTUAL1R(int, _export_post, Ref); +}; + +#endif // GLTF_DOCUMENT_EXTENSION_H diff --git a/modules/gltf/gltf_document_extension_convert_importer_mesh.cpp b/modules/gltf/gltf_document_extension_convert_importer_mesh.cpp new file mode 100644 index 000000000..1620900a0 --- /dev/null +++ b/modules/gltf/gltf_document_extension_convert_importer_mesh.cpp @@ -0,0 +1,81 @@ +/*************************************************************************/ +/* gltf_document_extension_convert_importer_mesh.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (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_extension_convert_importer_mesh.h" + +#include "gltf_state.h" + +#include "core/error/error_macros.h" +#include "scene/3d/mesh_instance_3d.h" +#include "scene/resources/importer_mesh.h" + +void GLTFDocumentExtensionConvertImporterMesh::_bind_methods() { +} + +Error GLTFDocumentExtensionConvertImporterMesh::import_post(Ref p_state, Node *p_root) { + ERR_FAIL_NULL_V(p_root, ERR_INVALID_PARAMETER); + ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); + List queue; + queue.push_back(p_root); + List delete_queue; + while (!queue.is_empty()) { + List::Element *E = queue.front(); + Node *node = E->get(); + ImporterMeshInstance3D *mesh_3d = cast_to(node); + if (mesh_3d) { + MeshInstance3D *mesh_instance_node_3d = memnew(MeshInstance3D); + Ref mesh = mesh_3d->get_mesh(); + if (mesh.is_valid()) { + Ref array_mesh = mesh->get_mesh(); + mesh_instance_node_3d->set_name(node->get_name()); + mesh_instance_node_3d->set_transform(mesh_3d->get_transform()); + mesh_instance_node_3d->set_mesh(array_mesh); + mesh_instance_node_3d->set_skin(mesh_3d->get_skin()); + mesh_instance_node_3d->set_skeleton_path(mesh_3d->get_skeleton_path()); + node->replace_by(mesh_instance_node_3d); + delete_queue.push_back(node); + } else { + memdelete(mesh_instance_node_3d); + } + } + int child_count = node->get_child_count(); + for (int i = 0; i < child_count; i++) { + queue.push_back(node->get_child(i)); + } + queue.pop_front(); + } + while (!queue.is_empty()) { + List::Element *E = delete_queue.front(); + Node *node = E->get(); + memdelete(node); + delete_queue.pop_front(); + } + return OK; +} diff --git a/modules/gltf/gltf_document_extension_convert_importer_mesh.h b/modules/gltf/gltf_document_extension_convert_importer_mesh.h new file mode 100644 index 000000000..00e664e73 --- /dev/null +++ b/modules/gltf/gltf_document_extension_convert_importer_mesh.h @@ -0,0 +1,50 @@ +/*************************************************************************/ +/* gltf_document_extension_convert_importer_mesh.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (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_EXTENSION_CONVERT_IMPORTER_MESH_H +#define GLTF_DOCUMENT_EXTENSION_CONVERT_IMPORTER_MESH_H + +#include "gltf_document_extension.h" + +#include "scene/3d/importer_mesh_instance_3d.h" +#include "scene/3d/mesh_instance_3d.h" +#include "scene/resources/importer_mesh.h" + +class GLTFDocumentExtensionConvertImporterMesh : public GLTFDocumentExtension { + GDCLASS(GLTFDocumentExtensionConvertImporterMesh, GLTFDocumentExtension); + +protected: + static void _bind_methods(); + +public: + Error import_post(Ref p_state, Node *p_root) override; +}; + +#endif // GLTF_DOCUMENT_EXTENSION_CONVERT_IMPORTER_MESH_H diff --git a/modules/gltf/gltf_state.cpp b/modules/gltf/gltf_state.cpp index 82beb2be8..a5f7bcf9d 100644 --- a/modules/gltf/gltf_state.cpp +++ b/modules/gltf/gltf_state.cpp @@ -30,8 +30,6 @@ #include "gltf_state.h" -#include "scene/resources/skin.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); @@ -59,6 +57,8 @@ void GLTFState::_bind_methods() { 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_base_path"), &GLTFState::get_base_path); + ClassDB::bind_method(D_METHOD("set_base_path", "base_path"), &GLTFState::set_base_path); 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); @@ -86,7 +86,7 @@ void GLTFState::_bind_methods() { 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::POOL_BYTE_ARRAY, "glb_data"), "set_glb_data", "get_glb_data"); // Vector + ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "glb_data"), "set_glb_data", "get_glb_data"); // Vector 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> ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "buffers"), "set_buffers", "get_buffers"); // Vector @@ -95,7 +95,8 @@ void GLTFState::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "meshes", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_meshes", "get_meshes"); // Vector> ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_materials", "get_materials"); // Vector ADD_PROPERTY(PropertyInfo(Variant::STRING, "scene_name"), "set_scene_name", "get_scene_name"); // String - ADD_PROPERTY(PropertyInfo(Variant::POOL_INT_ARRAY, "root_nodes"), "set_root_nodes", "get_root_nodes"); // Vector + ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_path"), "set_base_path", "get_base_path"); // String + ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "root_nodes"), "set_root_nodes", "get_root_nodes"); // Vector ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "textures", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_textures", "get_textures"); // Vector> ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "images", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_images", "get_images"); // Vector ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "skins", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_skins", "get_skins"); // Vector> @@ -104,7 +105,7 @@ void GLTFState::_bind_methods() { 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 ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "unique_animation_names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_unique_animation_names", "get_unique_animation_names"); // Set ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "skeletons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_skeletons", "get_skeletons"); // Vector> - 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> } @@ -149,51 +150,51 @@ void GLTFState::set_use_named_skin_binds(bool p_use_named_skin_binds) { } Array GLTFState::get_nodes() { - return GLTFDocument::to_array(nodes); + return GLTFTemplateConvert::to_array(nodes); } void GLTFState::set_nodes(Array p_nodes) { - GLTFDocument::set_from_array(nodes, p_nodes); + GLTFTemplateConvert::set_from_array(nodes, p_nodes); } Array GLTFState::get_buffers() { - return GLTFDocument::to_array(buffers); + return GLTFTemplateConvert::to_array(buffers); } void GLTFState::set_buffers(Array p_buffers) { - GLTFDocument::set_from_array(buffers, p_buffers); + GLTFTemplateConvert::set_from_array(buffers, p_buffers); } Array GLTFState::get_buffer_views() { - return GLTFDocument::to_array(buffer_views); + return GLTFTemplateConvert::to_array(buffer_views); } void GLTFState::set_buffer_views(Array p_buffer_views) { - GLTFDocument::set_from_array(buffer_views, p_buffer_views); + GLTFTemplateConvert::set_from_array(buffer_views, p_buffer_views); } Array GLTFState::get_accessors() { - return GLTFDocument::to_array(accessors); + return GLTFTemplateConvert::to_array(accessors); } void GLTFState::set_accessors(Array p_accessors) { - GLTFDocument::set_from_array(accessors, p_accessors); + GLTFTemplateConvert::set_from_array(accessors, p_accessors); } Array GLTFState::get_meshes() { - return GLTFDocument::to_array(meshes); + return GLTFTemplateConvert::to_array(meshes); } void GLTFState::set_meshes(Array p_meshes) { - GLTFDocument::set_from_array(meshes, p_meshes); + GLTFTemplateConvert::set_from_array(meshes, p_meshes); } Array GLTFState::get_materials() { - return GLTFDocument::to_array(materials); + return GLTFTemplateConvert::to_array(materials); } void GLTFState::set_materials(Array p_materials) { - GLTFDocument::set_from_array(materials, p_materials); + GLTFTemplateConvert::set_from_array(materials, p_materials); } String GLTFState::get_scene_name() { @@ -205,91 +206,91 @@ void GLTFState::set_scene_name(String p_scene_name) { } Array GLTFState::get_root_nodes() { - return GLTFDocument::to_array(root_nodes); + return GLTFTemplateConvert::to_array(root_nodes); } void GLTFState::set_root_nodes(Array p_root_nodes) { - GLTFDocument::set_from_array(root_nodes, p_root_nodes); + GLTFTemplateConvert::set_from_array(root_nodes, p_root_nodes); } Array GLTFState::get_textures() { - return GLTFDocument::to_array(textures); + return GLTFTemplateConvert::to_array(textures); } void GLTFState::set_textures(Array p_textures) { - GLTFDocument::set_from_array(textures, p_textures); + GLTFTemplateConvert::set_from_array(textures, p_textures); } Array GLTFState::get_images() { - return GLTFDocument::to_array(images); + return GLTFTemplateConvert::to_array(images); } void GLTFState::set_images(Array p_images) { - GLTFDocument::set_from_array(images, p_images); + GLTFTemplateConvert::set_from_array(images, p_images); } Array GLTFState::get_skins() { - return GLTFDocument::to_array(skins); + return GLTFTemplateConvert::to_array(skins); } void GLTFState::set_skins(Array p_skins) { - GLTFDocument::set_from_array(skins, p_skins); + GLTFTemplateConvert::set_from_array(skins, p_skins); } Array GLTFState::get_cameras() { - return GLTFDocument::to_array(cameras); + return GLTFTemplateConvert::to_array(cameras); } void GLTFState::set_cameras(Array p_cameras) { - GLTFDocument::set_from_array(cameras, p_cameras); + GLTFTemplateConvert::set_from_array(cameras, p_cameras); } Array GLTFState::get_lights() { - return GLTFDocument::to_array(lights); + return GLTFTemplateConvert::to_array(lights); } void GLTFState::set_lights(Array p_lights) { - GLTFDocument::set_from_array(lights, p_lights); + GLTFTemplateConvert::set_from_array(lights, p_lights); } Array GLTFState::get_unique_names() { - return GLTFDocument::to_array(unique_names); + return GLTFTemplateConvert::to_array(unique_names); } void GLTFState::set_unique_names(Array p_unique_names) { - GLTFDocument::set_from_array(unique_names, p_unique_names); + GLTFTemplateConvert::set_from_array(unique_names, p_unique_names); } Array GLTFState::get_unique_animation_names() { - return GLTFDocument::to_array(unique_animation_names); + return GLTFTemplateConvert::to_array(unique_animation_names); } void GLTFState::set_unique_animation_names(Array p_unique_animation_names) { - GLTFDocument::set_from_array(unique_animation_names, p_unique_animation_names); + GLTFTemplateConvert::set_from_array(unique_animation_names, p_unique_animation_names); } Array GLTFState::get_skeletons() { - return GLTFDocument::to_array(skeletons); + return GLTFTemplateConvert::to_array(skeletons); } void GLTFState::set_skeletons(Array p_skeletons) { - GLTFDocument::set_from_array(skeletons, p_skeletons); + GLTFTemplateConvert::set_from_array(skeletons, p_skeletons); } Dictionary GLTFState::get_skeleton_to_node() { - return GLTFDocument::to_dict(skeleton_to_node); + return GLTFTemplateConvert::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); + GLTFTemplateConvert::set_from_dict(skeleton_to_node, p_skeleton_to_node); } Array GLTFState::get_animations() { - return GLTFDocument::to_array(animations); + return GLTFTemplateConvert::to_array(animations); } void GLTFState::set_animations(Array p_animations) { - GLTFDocument::set_from_array(animations, p_animations); + GLTFTemplateConvert::set_from_array(animations, p_animations); } Node *GLTFState::get_scene_node(GLTFNodeIndex idx) { @@ -307,3 +308,19 @@ AnimationPlayer *GLTFState::get_animation_player(int idx) { ERR_FAIL_INDEX_V(idx, animation_players.size(), nullptr); return animation_players[idx]; } + +void GLTFState::set_discard_meshes_and_materials(bool p_discard_meshes_and_materials) { + discard_meshes_and_materials = p_discard_meshes_and_materials; +} + +bool GLTFState::get_discard_meshes_and_materials() { + return discard_meshes_and_materials; +} + +String GLTFState::get_base_path() { + return base_path; +} + +void GLTFState::set_base_path(String p_base_path) { + base_path = p_base_path; +} diff --git a/modules/gltf/gltf_state.h b/modules/gltf/gltf_state.h index cc33c3a0b..d2a4948f0 100644 --- a/modules/gltf/gltf_state.h +++ b/modules/gltf/gltf_state.h @@ -1,5 +1,3 @@ -#ifndef GLTF_STATE_H -#define GLTF_STATE_H /*************************************************************************/ /* gltf_state.h */ /*************************************************************************/ @@ -30,38 +28,39 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "core/map.h" -#include "core/resource.h" -#include "core/vector.h" +#ifndef GLTF_STATE_H +#define GLTF_STATE_H + +#include "extensions/gltf_light.h" +#include "gltf_template_convert.h" +#include "structures/gltf_accessor.h" +#include "structures/gltf_animation.h" +#include "structures/gltf_buffer_view.h" +#include "structures/gltf_camera.h" +#include "structures/gltf_mesh.h" +#include "structures/gltf_node.h" +#include "structures/gltf_skeleton.h" +#include "structures/gltf_skin.h" +#include "structures/gltf_texture.h" + +#include "core/templates/rb_map.h" #include "scene/animation/animation_player.h" #include "scene/resources/texture.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" - class GLTFState : public Resource { GDCLASS(GLTFState, Resource); friend class GLTFDocument; - friend class PackedSceneGLTF; String filename; + String base_path; Dictionary json; int major_version = 0; int minor_version = 0; Vector glb_data; bool use_named_skin_binds = false; - bool use_legacy_names = false; - uint32_t compress_flags = 0; + bool use_khr_texture_transform = false; + bool discard_meshes_and_materials = false; Vector> nodes; Vector> buffers; @@ -71,27 +70,27 @@ class GLTFState : public Resource { Vector> meshes; // meshes are loaded directly, no reason not to. Vector animation_players; - Map, GLTFMaterialIndex> material_cache; - Vector> materials; + HashMap, GLTFMaterialIndex> material_cache; + Vector> materials; String scene_name; Vector root_nodes; Vector> textures; - Vector> images; + Vector> images; Vector> skins; Vector> cameras; Vector> lights; - Set unique_names; - Set unique_animation_names; + HashSet unique_names; + HashSet unique_animation_names; Vector> skeletons; - Map skeleton_to_node; + HashMap skeleton_to_node; Vector> animations; - Map scene_nodes; + HashMap scene_nodes; - Map skeleton3d_to_gltf_skeleton; - Map> skin_and_skeleton3d_to_gltf_skin; + HashMap skeleton3d_to_gltf_skeleton; + HashMap> skin_and_skeleton3d_to_gltf_skin; protected: static void _bind_methods(); @@ -112,6 +111,9 @@ public: bool get_use_named_skin_binds(); void set_use_named_skin_binds(bool p_use_named_skin_binds); + bool get_discard_meshes_and_materials(); + void set_discard_meshes_and_materials(bool p_discard_meshes_and_materials); + Array get_nodes(); void set_nodes(Array p_nodes); @@ -133,6 +135,9 @@ public: String get_scene_name(); void set_scene_name(String p_scene_name); + String get_base_path(); + void set_base_path(String p_base_path); + Array get_root_nodes(); void set_root_nodes(Array p_root_nodes); @@ -171,6 +176,21 @@ public: int get_animation_players_count(int idx); AnimationPlayer *get_animation_player(int idx); + + //void set_scene_nodes(RBMap p_scene_nodes) { + // this->scene_nodes = p_scene_nodes; + //} + + //void set_animation_players(Vector p_animation_players) { + // this->animation_players = p_animation_players; + //} + + //RBMap, GLTFMaterialIndex> get_material_cache() { + // return this->material_cache; + //} + //void set_material_cache(RBMap, GLTFMaterialIndex> p_material_cache) { + // this->material_cache = p_material_cache; + //} }; #endif // GLTF_STATE_H diff --git a/modules/gltf/gltf_template_convert.h b/modules/gltf/gltf_template_convert.h new file mode 100644 index 000000000..c915d3deb --- /dev/null +++ b/modules/gltf/gltf_template_convert.h @@ -0,0 +1,94 @@ +/*************************************************************************/ +/* gltf_template_convert.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (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_TEMPLATE_CONVERT_H +#define GLTF_TEMPLATE_CONVERT_H + +#include "core/templates/hash_set.h" +#include "core/variant/array.h" +#include "core/variant/dictionary.h" + +namespace GLTFTemplateConvert { +template +static Array to_array(const Vector &p_inp) { + Array ret; + for (int i = 0; i < p_inp.size(); i++) { + ret.push_back(p_inp[i]); + } + return ret; +} + +template +static Array to_array(const HashSet &p_inp) { + Array ret; + typename HashSet::Iterator elem = p_inp.begin(); + while (elem) { + ret.push_back(*elem); + ++elem; + } + return ret; +} + +template +static void set_from_array(Vector &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 +static void set_from_array(HashSet &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 +static Dictionary to_dict(const HashMap &p_inp) { + Dictionary ret; + for (const KeyValue &E : p_inp) { + ret[E.key] = E.value; + } + return ret; +} + +template +static void set_from_dict(HashMap &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]]; + } +} +} //namespace GLTFTemplateConvert + +#endif // GLTF_TEMPLATE_CONVERT_H diff --git a/modules/gltf/packed_scene_gltf.cpp b/modules/gltf/packed_scene_gltf.cpp deleted file mode 100644 index fc1daea50..000000000 --- a/modules/gltf/packed_scene_gltf.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/*************************************************************************/ -/* packed_scene_gltf.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (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 TOOLS_ENABLED - -#include "packed_scene_gltf.h" - -#include "editor/import/resource_importer_scene.h" -#include "scene/3d/spatial.h" -#include "scene/animation/animation_player.h" -#include "scene/resources/mesh.h" -#include "scene/resources/skin.h" - -#include "gltf_document.h" - -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", "compress_flags", "state"), - &PackedSceneGLTF::pack_gltf, DEFVAL(0), DEFVAL(1000.0f), DEFVAL(Mesh::ARRAY_COMPRESS_DEFAULT), DEFVAL(Ref())); - ClassDB::bind_method(D_METHOD("import_gltf_scene", "path", "flags", "bake_fps", "compress_flags", "state"), - &PackedSceneGLTF::import_gltf_scene, DEFVAL(0), DEFVAL(1000.0f), DEFVAL(Mesh::ARRAY_COMPRESS_DEFAULT), DEFVAL(Ref())); -} - -Node *PackedSceneGLTF::import_gltf_scene(const String &p_path, uint32_t p_flags, float p_bake_fps, uint32_t p_compress_flags, Ref r_state) { - Error err = FAILED; - List deps; - return import_scene(p_path, p_flags, p_bake_fps, p_compress_flags, &deps, &err, r_state); -} - -Node *PackedSceneGLTF::import_scene(const String &p_path, uint32_t p_flags, - int p_bake_fps, uint32_t p_compress_flags, - List *r_missing_deps, - Error *r_err, - Ref r_state) { - if (r_state == Ref()) { - r_state.instance(); - } - r_state->use_named_skin_binds = - p_flags & EditorSceneImporter::IMPORT_USE_NAMED_SKIN_BINDS; - r_state->use_legacy_names = - p_flags & EditorSceneImporter::IMPORT_USE_LEGACY_NAMES; - r_state->compress_flags = p_compress_flags; - - Ref 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); - - Spatial *root = memnew(Spatial); - if (r_state->use_legacy_names) { - root->set_name(gltf_document->_legacy_validate_node_name(r_state->scene_name)); - } else { - root->set_name(r_state->scene_name); - } - 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(root); -} - -void PackedSceneGLTF::pack_gltf(String p_path, int32_t p_flags, - real_t p_bake_fps, uint32_t p_compress_flags, Ref r_state) { - Error err = FAILED; - List deps; - Node *root = import_scene(p_path, p_flags, p_bake_fps, p_compress_flags, &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 *r_missing_deps, - Error *r_err) { - Error err = FAILED; - if (r_err) { - *r_err = err; - } - Ref gltf_document; - gltf_document.instance(); - Ref state; - state.instance(); - err = gltf_document->serialize(state, p_node, p_path); - if (r_err) { - *r_err = err; - } -} - -void PackedSceneGLTF::_build_parent_hierachy(Ref 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 deps; - Error err; - String path = p_path; - int32_t flags = p_flags; - real_t baked_fps = p_bake_fps; - Ref 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; -} - -#endif // TOOLS_ENABLED diff --git a/modules/gltf/register_types.cpp b/modules/gltf/register_types.cpp index 4ce376eaf..791c2c0ee 100644 --- a/modules/gltf/register_types.cpp +++ b/modules/gltf/register_types.cpp @@ -28,66 +28,131 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef _3D_DISABLED - #include "register_types.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" -#include "packed_scene_gltf.h" - -#ifdef TOOLS_ENABLED -#include "editor/editor_node.h" -#include "editor_scene_exporter_gltf_plugin.h" -#include "editor_scene_importer_gltf.h" -#endif - -#ifdef TOOLS_ENABLED -static void _editor_init() { - Ref import_gltf; - import_gltf.instance(); - ResourceImporterScene::get_singleton()->add_importer(import_gltf); -} -#endif +#ifndef _3D_DISABLED void register_gltf_types() { -#ifdef TOOLS_ENABLED - ClassDB::APIType prev_api = ClassDB::get_current_api(); - ClassDB::set_current_api(ClassDB::API_EDITOR); - ClassDB::register_class(); - ClassDB::register_class(); - EditorPlugins::add_by_type(); - ClassDB::set_current_api(prev_api); - EditorNode::add_init_callback(_editor_init); -#endif - ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); } - void unregister_gltf_types() { + } +/* +#include "extensions/gltf_light.h" +#include "extensions/gltf_spec_gloss.h" +#include "gltf_document.h" +#include "gltf_document_extension.h" +#include "gltf_document_extension_convert_importer_mesh.h" +#include "gltf_state.h" +#include "structures/gltf_accessor.h" +#include "structures/gltf_animation.h" +#include "structures/gltf_buffer_view.h" +#include "structures/gltf_camera.h" +#include "structures/gltf_mesh.h" +#include "structures/gltf_node.h" +#include "structures/gltf_skeleton.h" +#include "structures/gltf_skin.h" +#include "structures/gltf_texture.h" + +#ifdef TOOLS_ENABLED +#include "core/config/project_settings.h" +#include "editor/editor_node.h" +#include "editor/editor_scene_exporter_gltf_plugin.h" +#include "editor/editor_scene_importer_blend.h" +#include "editor/editor_scene_importer_fbx.h" +#include "editor/editor_scene_importer_gltf.h" +#include "editor/editor_settings.h" + +static void _editor_init() { + Ref import_gltf; + import_gltf.instantiate(); + ResourceImporterScene::add_importer(import_gltf); + + // Blend to glTF importer. + + bool blend_enabled = GLOBAL_GET("filesystem/import/blender/enabled"); + // Defined here because EditorSettings doesn't exist in `register_gltf_types` yet. + EDITOR_DEF_RST("filesystem/import/blender/blender3_path", ""); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, + "filesystem/import/blender/blender3_path", PROPERTY_HINT_GLOBAL_DIR)); + if (blend_enabled) { + Ref importer; + importer.instantiate(); + ResourceImporterScene::add_importer(importer); + + Ref blend_import_query; + blend_import_query.instantiate(); + EditorFileSystem::get_singleton()->add_import_format_support_query(blend_import_query); + } + + // FBX to glTF importer. + + bool fbx_enabled = GLOBAL_GET("filesystem/import/fbx/enabled"); + // Defined here because EditorSettings doesn't exist in `register_gltf_types` yet. + String fbx2gltf_path = EDITOR_DEF_RST("filesystem/import/fbx/fbx2gltf_path", ""); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, + "filesystem/import/fbx/fbx2gltf_path", PROPERTY_HINT_GLOBAL_FILE)); + if (fbx_enabled) { + Ref da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + if (fbx2gltf_path.is_empty()) { + WARN_PRINT("FBX file import is enabled, but no FBX2glTF path is configured. FBX files will not be imported."); + } else if (!da->file_exists(fbx2gltf_path)) { + WARN_PRINT("FBX file import is enabled, but the FBX2glTF path doesn't point to a valid FBX2glTF executable. FBX files will not be imported."); + } else { + Ref importer; + importer.instantiate(); + ResourceImporterScene::add_importer(importer); + } + } +} +#endif // TOOLS_ENABLED + +void initialize_gltf_module() { + if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) { + // glTF API available at runtime. + GDREGISTER_CLASS(GLTFAccessor); + GDREGISTER_CLASS(GLTFAnimation); + GDREGISTER_CLASS(GLTFBufferView); + GDREGISTER_CLASS(GLTFCamera); + GDREGISTER_CLASS(GLTFDocument); + GDREGISTER_CLASS(GLTFDocumentExtension); + GDREGISTER_CLASS(GLTFDocumentExtensionConvertImporterMesh); + GDREGISTER_CLASS(GLTFLight); + GDREGISTER_CLASS(GLTFMesh); + GDREGISTER_CLASS(GLTFNode); + GDREGISTER_CLASS(GLTFSkeleton); + GDREGISTER_CLASS(GLTFSkin); + GDREGISTER_CLASS(GLTFSpecGloss); + GDREGISTER_CLASS(GLTFState); + GDREGISTER_CLASS(GLTFTexture); + } + +#ifdef TOOLS_ENABLED + if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) { + // Editor-specific API. + ClassDB::APIType prev_api = ClassDB::get_current_api(); + ClassDB::set_current_api(ClassDB::API_EDITOR); + + GDREGISTER_CLASS(EditorSceneFormatImporterGLTF); + EditorPlugins::add_by_type(); + + // Project settings defined here so doctool finds them. + GLOBAL_DEF_RST("filesystem/import/blender/enabled", true); + GLOBAL_DEF_RST("filesystem/import/fbx/enabled", true); + GDREGISTER_CLASS(EditorSceneFormatImporterBlend); + GDREGISTER_CLASS(EditorSceneFormatImporterFBX); + + ClassDB::set_current_api(prev_api); + EditorNode::add_init_callback(_editor_init); + } + +#endif // TOOLS_ENABLED +} + +void uninitialize_gltf_module() { +} +*/ + #endif // _3D_DISABLED diff --git a/modules/gltf/register_types.h b/modules/gltf/register_types.h index b3cdad7c9..4a9c31241 100644 --- a/modules/gltf/register_types.h +++ b/modules/gltf/register_types.h @@ -1,5 +1,3 @@ -#ifndef GLTF_REGISTER_TYPES_H -#define GLTF_REGISTER_TYPES_H /*************************************************************************/ /* register_types.h */ /*************************************************************************/ @@ -30,11 +28,5 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef _3D_DISABLED - void register_gltf_types(); void unregister_gltf_types(); - -#endif // _3D_DISABLED - -#endif // GLTF_REGISTER_TYPES_H diff --git a/modules/gltf/gltf_accessor.cpp b/modules/gltf/structures/gltf_accessor.cpp similarity index 94% rename from modules/gltf/gltf_accessor.cpp rename to modules/gltf/structures/gltf_accessor.cpp index fd29ac2e8..1b8911fe7 100644 --- a/modules/gltf/gltf_accessor.cpp +++ b/modules/gltf/structures/gltf_accessor.cpp @@ -65,9 +65,9 @@ void GLTFAccessor::_bind_methods() { 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::POOL_REAL_ARRAY, "min"), "set_min", "get_min"); // Vector - ADD_PROPERTY(PropertyInfo(Variant::POOL_REAL_ARRAY, "max"), "set_max", "get_max"); // Vector + ADD_PROPERTY(PropertyInfo(Variant::INT, "type"), "set_type", "get_type"); // GLTFType + ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT64_ARRAY, "min"), "set_min", "get_min"); // Vector + ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT64_ARRAY, "max"), "set_max", "get_max"); // Vector 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 @@ -121,22 +121,22 @@ int GLTFAccessor::get_type() { } void GLTFAccessor::set_type(int p_type) { - type = (GLTFDocument::GLTFType)p_type; // TODO: Register enum + type = (GLTFType)p_type; // TODO: Register enum } -PoolVector GLTFAccessor::get_min() { +Vector GLTFAccessor::get_min() { return min; } -void GLTFAccessor::set_min(PoolVector p_min) { +void GLTFAccessor::set_min(Vector p_min) { min = p_min; } -PoolVector GLTFAccessor::get_max() { +Vector GLTFAccessor::get_max() { return max; } -void GLTFAccessor::set_max(PoolVector p_max) { +void GLTFAccessor::set_max(Vector p_max) { max = p_max; } diff --git a/modules/gltf/gltf_accessor.h b/modules/gltf/structures/gltf_accessor.h similarity index 92% rename from modules/gltf/gltf_accessor.h rename to modules/gltf/structures/gltf_accessor.h index 6e6a6007b..bfb71d57f 100644 --- a/modules/gltf/gltf_accessor.h +++ b/modules/gltf/structures/gltf_accessor.h @@ -1,5 +1,3 @@ -#ifndef GLTF_ACCESSOR_H -#define GLTF_ACCESSOR_H /*************************************************************************/ /* gltf_accessor.h */ /*************************************************************************/ @@ -30,9 +28,12 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "core/resource.h" +#ifndef GLTF_ACCESSOR_H +#define GLTF_ACCESSOR_H -#include "gltf_document.h" +#include "core/io/resource.h" + +#include "../gltf_defines.h" struct GLTFAccessor : public Resource { GDCLASS(GLTFAccessor, Resource); @@ -44,9 +45,9 @@ private: int component_type = 0; bool normalized = false; int count = 0; - GLTFDocument::GLTFType type = GLTFDocument::TYPE_SCALAR; - PoolVector min; - PoolVector max; + GLTFType type = GLTFType::TYPE_SCALAR; + Vector min; + Vector max; int sparse_count = 0; int sparse_indices_buffer_view = 0; int sparse_indices_byte_offset = 0; @@ -76,11 +77,11 @@ public: int get_type(); void set_type(int p_type); - PoolVector get_min(); - void set_min(PoolVector p_min); + Vector get_min(); + void set_min(Vector p_min); - PoolVector get_max(); - void set_max(PoolVector p_max); + Vector get_max(); + void set_max(Vector p_max); int get_sparse_count(); void set_sparse_count(int p_sparse_count); diff --git a/modules/gltf/gltf_animation.cpp b/modules/gltf/structures/gltf_animation.cpp similarity index 97% rename from modules/gltf/gltf_animation.cpp rename to modules/gltf/structures/gltf_animation.cpp index c857be4b2..e598c870a 100644 --- a/modules/gltf/gltf_animation.cpp +++ b/modules/gltf/structures/gltf_animation.cpp @@ -45,7 +45,7 @@ void GLTFAnimation::set_loop(bool p_val) { loop = p_val; } -Map &GLTFAnimation::get_tracks() { +HashMap &GLTFAnimation::get_tracks() { return tracks; } diff --git a/modules/gltf/gltf_animation.h b/modules/gltf/structures/gltf_animation.h similarity index 91% rename from modules/gltf/gltf_animation.h rename to modules/gltf/structures/gltf_animation.h index 008782ae7..3777f579f 100644 --- a/modules/gltf/gltf_animation.h +++ b/modules/gltf/structures/gltf_animation.h @@ -1,5 +1,3 @@ -#ifndef GLTF_ANIMATION_H -#define GLTF_ANIMATION_H /*************************************************************************/ /* gltf_animation.h */ /*************************************************************************/ @@ -30,7 +28,10 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "core/resource.h" +#ifndef GLTF_ANIMATION_H +#define GLTF_ANIMATION_H + +#include "core/io/resource.h" class GLTFAnimation : public Resource { GDCLASS(GLTFAnimation, Resource); @@ -49,26 +50,26 @@ public: template struct Channel { Interpolation interpolation; - Vector times; + Vector times; Vector values; }; struct Track { - Channel translation_track; - Channel rotation_track; + Channel position_track; + Channel rotation_track; Channel scale_track; - Vector> weight_tracks; + Vector> weight_tracks; }; public: bool get_loop() const; void set_loop(bool p_val); - Map &get_tracks(); + HashMap &get_tracks(); GLTFAnimation(); private: bool loop = false; - Map tracks; + HashMap tracks; }; #endif // GLTF_ANIMATION_H diff --git a/modules/gltf/gltf_buffer_view.cpp b/modules/gltf/structures/gltf_buffer_view.cpp similarity index 99% rename from modules/gltf/gltf_buffer_view.cpp rename to modules/gltf/structures/gltf_buffer_view.cpp index a15141225..ba19ed862 100644 --- a/modules/gltf/gltf_buffer_view.cpp +++ b/modules/gltf/structures/gltf_buffer_view.cpp @@ -30,6 +30,8 @@ #include "gltf_buffer_view.h" +#include "../gltf_document_extension.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); diff --git a/modules/gltf/gltf_buffer_view.h b/modules/gltf/structures/gltf_buffer_view.h similarity index 98% rename from modules/gltf/gltf_buffer_view.h rename to modules/gltf/structures/gltf_buffer_view.h index e148a65c0..b1f500de2 100644 --- a/modules/gltf/gltf_buffer_view.h +++ b/modules/gltf/structures/gltf_buffer_view.h @@ -1,5 +1,3 @@ -#ifndef GLTF_BUFFER_VIEW_H -#define GLTF_BUFFER_VIEW_H /*************************************************************************/ /* gltf_buffer_view.h */ /*************************************************************************/ @@ -30,9 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "core/resource.h" +#ifndef GLTF_BUFFER_VIEW_H +#define GLTF_BUFFER_VIEW_H -#include "gltf_document.h" +#include "../gltf_defines.h" +#include "core/io/resource.h" class GLTFBufferView : public Resource { GDCLASS(GLTFBufferView, Resource); diff --git a/modules/gltf/gltf_camera.cpp b/modules/gltf/structures/gltf_camera.cpp similarity index 80% rename from modules/gltf/gltf_camera.cpp rename to modules/gltf/structures/gltf_camera.cpp index 0efdfb06f..f3ea6a1c4 100644 --- a/modules/gltf/gltf_camera.cpp +++ b/modules/gltf/structures/gltf_camera.cpp @@ -35,13 +35,13 @@ void GLTFCamera::_bind_methods() { 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); + ClassDB::bind_method(D_METHOD("get_depth_far"), &GLTFCamera::get_depth_far); + ClassDB::bind_method(D_METHOD("set_depth_far", "zdepth_far"), &GLTFCamera::set_depth_far); + ClassDB::bind_method(D_METHOD("get_depth_near"), &GLTFCamera::get_depth_near); + ClassDB::bind_method(D_METHOD("set_depth_near", "zdepth_near"), &GLTFCamera::set_depth_near); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "perspective"), "set_perspective", "get_perspective"); // bool - ADD_PROPERTY(PropertyInfo(Variant::REAL, "fov_size"), "set_fov_size", "get_fov_size"); // float - ADD_PROPERTY(PropertyInfo(Variant::REAL, "zfar"), "set_zfar", "get_zfar"); // float - ADD_PROPERTY(PropertyInfo(Variant::REAL, "znear"), "set_znear", "get_znear"); // float + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fov_size"), "set_fov_size", "get_fov_size"); // float + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth_far"), "set_depth_far", "get_depth_far"); // float + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth_near"), "set_depth_near", "get_depth_near"); // float } diff --git a/modules/gltf/gltf_camera.h b/modules/gltf/structures/gltf_camera.h similarity index 89% rename from modules/gltf/gltf_camera.h rename to modules/gltf/structures/gltf_camera.h index 207dff64e..b7df74182 100644 --- a/modules/gltf/gltf_camera.h +++ b/modules/gltf/structures/gltf_camera.h @@ -1,5 +1,3 @@ -#ifndef GLTF_CAMERA_H -#define GLTF_CAMERA_H /*************************************************************************/ /* gltf_camera.h */ /*************************************************************************/ @@ -30,7 +28,10 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "core/resource.h" +#ifndef GLTF_CAMERA_H +#define GLTF_CAMERA_H + +#include "core/io/resource.h" class GLTFCamera : public Resource { GDCLASS(GLTFCamera, Resource); @@ -38,8 +39,8 @@ class GLTFCamera : public Resource { private: bool perspective = true; float fov_size = 75.0; - float zfar = 4000.0; - float znear = 0.05; + float depth_far = 4000.0; + float depth_near = 0.05; protected: static void _bind_methods(); @@ -49,10 +50,10 @@ public: 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; } + float get_depth_far() const { return depth_far; } + void set_depth_far(float p_val) { depth_far = p_val; } + float get_depth_near() const { return depth_near; } + void set_depth_near(float p_val) { depth_near = p_val; } }; #endif // GLTF_CAMERA_H diff --git a/modules/gltf/gltf_mesh.cpp b/modules/gltf/structures/gltf_mesh.cpp similarity index 92% rename from modules/gltf/gltf_mesh.cpp rename to modules/gltf/structures/gltf_mesh.cpp index f73b022d5..3add8304b 100644 --- a/modules/gltf/gltf_mesh.cpp +++ b/modules/gltf/structures/gltf_mesh.cpp @@ -30,6 +30,8 @@ #include "gltf_mesh.h" +#include "scene/resources/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); @@ -39,15 +41,15 @@ void GLTFMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_instance_materials", "instance_materials"), &GLTFMesh::set_instance_materials); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh"), "set_mesh", "get_mesh"); - ADD_PROPERTY(PropertyInfo(Variant::POOL_REAL_ARRAY, "blend_weights"), "set_blend_weights", "get_blend_weights"); // Vector + ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT32_ARRAY, "blend_weights"), "set_blend_weights", "get_blend_weights"); // Vector ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "instance_materials"), "set_instance_materials", "get_instance_materials"); } -Ref GLTFMesh::get_mesh() { +Ref GLTFMesh::get_mesh() { return mesh; } -void GLTFMesh::set_mesh(Ref p_mesh) { +void GLTFMesh::set_mesh(Ref p_mesh) { mesh = p_mesh; } diff --git a/modules/gltf/gltf_mesh.h b/modules/gltf/structures/gltf_mesh.h similarity index 92% rename from modules/gltf/gltf_mesh.h rename to modules/gltf/structures/gltf_mesh.h index 780855b2c..dc26120b4 100644 --- a/modules/gltf/gltf_mesh.h +++ b/modules/gltf/structures/gltf_mesh.h @@ -1,5 +1,3 @@ -#ifndef GLTF_MESH_H -#define GLTF_MESH_H /*************************************************************************/ /* gltf_mesh.h */ /*************************************************************************/ @@ -30,14 +28,19 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "core/resource.h" +#ifndef GLTF_MESH_H +#define GLTF_MESH_H + +#include "core/io/resource.h" +#include "scene/3d/importer_mesh_instance_3d.h" +#include "scene/resources/importer_mesh.h" #include "scene/resources/mesh.h" class GLTFMesh : public Resource { GDCLASS(GLTFMesh, Resource); private: - Ref mesh; + Ref mesh; Vector blend_weights; Array instance_materials; @@ -45,8 +48,8 @@ protected: static void _bind_methods(); public: - Ref get_mesh(); - void set_mesh(Ref p_mesh); + Ref get_mesh(); + void set_mesh(Ref p_mesh); Vector get_blend_weights(); void set_blend_weights(Vector p_blend_weights); Array get_instance_materials(); diff --git a/modules/gltf/gltf_node.cpp b/modules/gltf/structures/gltf_node.cpp similarity index 87% rename from modules/gltf/gltf_node.cpp rename to modules/gltf/structures/gltf_node.cpp index 5324acb6e..86280603f 100644 --- a/modules/gltf/gltf_node.cpp +++ b/modules/gltf/structures/gltf_node.cpp @@ -47,8 +47,8 @@ void GLTFNode::_bind_methods() { 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_position"), &GLTFNode::get_position); + ClassDB::bind_method(D_METHOD("set_position", "position"), &GLTFNode::set_position); 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); @@ -60,16 +60,16 @@ void GLTFNode::_bind_methods() { 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::TRANSFORM3D, "xform"), "set_xform", "get_xform"); // Transform3D 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, "position"), "set_position", "get_position"); // Vector3 + ADD_PROPERTY(PropertyInfo(Variant::QUATERNION, "rotation"), "set_rotation", "get_rotation"); // Quaternion ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "scale"), "set_scale", "get_scale"); // Vector3 - ADD_PROPERTY(PropertyInfo(Variant::POOL_INT_ARRAY, "children"), "set_children", "get_children"); // Vector + ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "children"), "set_children", "get_children"); // Vector ADD_PROPERTY(PropertyInfo(Variant::INT, "light"), "set_light", "get_light"); // GLTFLightIndex } @@ -89,11 +89,11 @@ void GLTFNode::set_height(int p_height) { height = p_height; } -Transform GLTFNode::get_xform() { +Transform3D GLTFNode::get_xform() { return xform; } -void GLTFNode::set_xform(Transform p_xform) { +void GLTFNode::set_xform(Transform3D p_xform) { xform = p_xform; } @@ -137,19 +137,19 @@ void GLTFNode::set_joint(bool p_joint) { joint = p_joint; } -Vector3 GLTFNode::get_translation() { - return translation; +Vector3 GLTFNode::get_position() { + return position; } -void GLTFNode::set_translation(Vector3 p_translation) { - translation = p_translation; +void GLTFNode::set_position(Vector3 p_position) { + position = p_position; } -Quat GLTFNode::get_rotation() { +Quaternion GLTFNode::get_rotation() { return rotation; } -void GLTFNode::set_rotation(Quat p_rotation) { +void GLTFNode::set_rotation(Quaternion p_rotation) { rotation = p_rotation; } diff --git a/modules/gltf/gltf_node.h b/modules/gltf/structures/gltf_node.h similarity index 91% rename from modules/gltf/gltf_node.h rename to modules/gltf/structures/gltf_node.h index 8e407a4a5..1a57ea32e 100644 --- a/modules/gltf/gltf_node.h +++ b/modules/gltf/structures/gltf_node.h @@ -1,5 +1,3 @@ -#ifndef GLTF_NODE_H -#define GLTF_NODE_H /*************************************************************************/ /* gltf_node.h */ /*************************************************************************/ @@ -30,27 +28,28 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "core/resource.h" +#ifndef GLTF_NODE_H +#define GLTF_NODE_H -#include "gltf_document.h" +#include "../gltf_defines.h" +#include "core/io/resource.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; + Transform3D xform; GLTFMeshIndex mesh = -1; GLTFCameraIndex camera = -1; GLTFSkinIndex skin = -1; GLTFSkeletonIndex skeleton = -1; bool joint = false; - Vector3 translation; - Quat rotation; + Vector3 position; + Quaternion rotation; Vector3 scale = Vector3(1, 1, 1); Vector children; GLTFLightIndex light = -1; @@ -65,8 +64,8 @@ public: int get_height(); void set_height(int p_height); - Transform get_xform(); - void set_xform(Transform p_xform); + Transform3D get_xform(); + void set_xform(Transform3D p_xform); GLTFMeshIndex get_mesh(); void set_mesh(GLTFMeshIndex p_mesh); @@ -83,11 +82,11 @@ public: bool get_joint(); void set_joint(bool p_joint); - Vector3 get_translation(); - void set_translation(Vector3 p_translation); + Vector3 get_position(); + void set_position(Vector3 p_position); - Quat get_rotation(); - void set_rotation(Quat p_rotation); + Quaternion get_rotation(); + void set_rotation(Quaternion p_rotation); Vector3 get_scale(); void set_scale(Vector3 p_scale); diff --git a/modules/gltf/gltf_skeleton.cpp b/modules/gltf/structures/gltf_skeleton.cpp similarity index 69% rename from modules/gltf/gltf_skeleton.cpp rename to modules/gltf/structures/gltf_skeleton.cpp index 0d0e22ef4..90a6b0f50 100644 --- a/modules/gltf/gltf_skeleton.cpp +++ b/modules/gltf/structures/gltf_skeleton.cpp @@ -30,62 +30,65 @@ #include "gltf_skeleton.h" +#include "../gltf_template_convert.h" +#include "scene/3d/bone_attachment_3d.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_pandemonium_skeleton"), &GLTFSkeleton::get_pandemonium_skeleton); + 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_pandemonium_bone_node"), &GLTFSkeleton::get_pandemonium_bone_node); - ClassDB::bind_method(D_METHOD("set_pandemonium_bone_node", "pandemonium_bone_node"), &GLTFSkeleton::set_pandemonium_bone_node); + 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", "idx"), &GLTFSkeleton::get_bone_attachment); - ADD_PROPERTY(PropertyInfo(Variant::POOL_INT_ARRAY, "joints"), "set_joints", "get_joints"); // PoolVector - ADD_PROPERTY(PropertyInfo(Variant::POOL_INT_ARRAY, "roots"), "set_roots", "get_roots"); // PoolVector + ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "joints"), "set_joints", "get_joints"); // Vector + ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "roots"), "set_roots", "get_roots"); // Vector 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 - ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "pandemonium_bone_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_pandemonium_bone_node", "get_pandemonium_bone_node"); // Map GLTFSkeleton::get_joints() { +Vector GLTFSkeleton::get_joints() { return joints; } -void GLTFSkeleton::set_joints(PoolVector p_joints) { +void GLTFSkeleton::set_joints(Vector p_joints) { joints = p_joints; } -PoolVector GLTFSkeleton::get_roots() { +Vector GLTFSkeleton::get_roots() { return roots; } -void GLTFSkeleton::set_roots(PoolVector p_roots) { +void GLTFSkeleton::set_roots(Vector p_roots) { roots = p_roots; } -Skeleton *GLTFSkeleton::get_pandemonium_skeleton() { - return pandemonium_skeleton; +Skeleton3D *GLTFSkeleton::get_godot_skeleton() { + return godot_skeleton; } Array GLTFSkeleton::get_unique_names() { - return GLTFDocument::to_array(unique_names); + return GLTFTemplateConvert::to_array(unique_names); } void GLTFSkeleton::set_unique_names(Array p_unique_names) { - GLTFDocument::set_from_array(unique_names, p_unique_names); + GLTFTemplateConvert::set_from_array(unique_names, p_unique_names); } -Dictionary GLTFSkeleton::get_pandemonium_bone_node() { - return GLTFDocument::to_dict(pandemonium_bone_node); +Dictionary GLTFSkeleton::get_godot_bone_node() { + return GLTFTemplateConvert::to_dict(godot_bone_node); } -void GLTFSkeleton::set_pandemonium_bone_node(Dictionary p_indict) { - GLTFDocument::set_from_dict(pandemonium_bone_node, p_indict); +void GLTFSkeleton::set_godot_bone_node(Dictionary p_indict) { + GLTFTemplateConvert::set_from_dict(godot_bone_node, p_indict); } -BoneAttachment *GLTFSkeleton::get_bone_attachment(int idx) { +BoneAttachment3D *GLTFSkeleton::get_bone_attachment(int idx) { ERR_FAIL_INDEX_V(idx, bone_attachments.size(), nullptr); return bone_attachments[idx]; } diff --git a/modules/gltf/gltf_skeleton.h b/modules/gltf/structures/gltf_skeleton.h similarity index 67% rename from modules/gltf/gltf_skeleton.h rename to modules/gltf/structures/gltf_skeleton.h index 364731480..db8862321 100644 --- a/modules/gltf/gltf_skeleton.h +++ b/modules/gltf/structures/gltf_skeleton.h @@ -1,5 +1,3 @@ -#ifndef GLTF_SKELETON_H -#define GLTF_SKELETON_H /*************************************************************************/ /* gltf_skeleton.h */ /*************************************************************************/ @@ -30,9 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "core/resource.h" +#ifndef GLTF_SKELETON_H +#define GLTF_SKELETON_H -#include "gltf_document.h" +#include "../gltf_defines.h" +#include "core/io/resource.h" class GLTFSkeleton : public Resource { GDCLASS(GLTFSkeleton, Resource); @@ -40,41 +40,61 @@ class GLTFSkeleton : public Resource { private: // The *synthesized* skeletons joints - PoolVector joints; + Vector joints; // The roots of the skeleton. If there are multiple, each root must have the // same parent (ie roots are siblings) - PoolVector roots; + Vector roots; - // The created Skeleton for the scene - Skeleton *pandemonium_skeleton = nullptr; + // The created Skeleton3D for the scene + Skeleton3D *godot_skeleton = nullptr; // Set of unique bone names for the skeleton - Set unique_names; + HashSet unique_names; - Map pandemonium_bone_node; + HashMap godot_bone_node; - PoolVector bone_attachments; + Vector bone_attachments; protected: static void _bind_methods(); public: - PoolVector get_joints(); - void set_joints(PoolVector p_joints); + Vector get_joints(); + void set_joints(Vector p_joints); - PoolVector get_roots(); - void set_roots(PoolVector p_roots); + Vector get_roots(); + void set_roots(Vector p_roots); - Skeleton *get_pandemonium_skeleton(); + 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); - Dictionary get_pandemonium_bone_node(); - void set_pandemonium_bone_node(Dictionary p_indict); + //RBMap get_godot_bone_node() { + // return this->godot_bone_node; + //} + //void set_godot_bone_node(RBMap 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); - BoneAttachment *get_bone_attachment(int idx); + //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(); }; diff --git a/modules/gltf/gltf_skin.cpp b/modules/gltf/structures/gltf_skin.cpp similarity index 80% rename from modules/gltf/gltf_skin.cpp rename to modules/gltf/structures/gltf_skin.cpp index 5eacb2aa4..2e46ee3be 100644 --- a/modules/gltf/gltf_skin.cpp +++ b/modules/gltf/structures/gltf_skin.cpp @@ -30,6 +30,7 @@ #include "gltf_skin.h" +#include "../gltf_template_convert.h" #include "scene/resources/skin.h" void GLTFSkin::_bind_methods() { @@ -51,19 +52,19 @@ void GLTFSkin::_bind_methods() { 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_pandemonium_skin"), &GLTFSkin::get_pandemonium_skin); - ClassDB::bind_method(D_METHOD("set_pandemonium_skin", "pandemonium_skin"), &GLTFSkin::set_pandemonium_skin); + 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::POOL_INT_ARRAY, "joints_original"), "set_joints_original", "get_joints_original"); // Vector - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "inverse_binds", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL), "set_inverse_binds", "get_inverse_binds"); // Vector - ADD_PROPERTY(PropertyInfo(Variant::POOL_INT_ARRAY, "joints"), "set_joints", "get_joints"); // Vector - ADD_PROPERTY(PropertyInfo(Variant::POOL_INT_ARRAY, "non_joints"), "set_non_joints", "get_non_joints"); // Vector - ADD_PROPERTY(PropertyInfo(Variant::POOL_INT_ARRAY, "roots"), "set_roots", "get_roots"); // Vector + ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "joints_original"), "set_joints_original", "get_joints_original"); // Vector + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "inverse_binds", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL), "set_inverse_binds", "get_inverse_binds"); // Vector + ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "joints"), "set_joints", "get_joints"); // Vector + ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "non_joints"), "set_non_joints", "get_non_joints"); // Vector + ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "roots"), "set_roots", "get_roots"); // Vector 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 + 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"); // RBMap } GLTFNodeIndex GLTFSkin::get_skin_root() { @@ -83,11 +84,11 @@ void GLTFSkin::set_joints_original(Vector p_joints_original) { } Array GLTFSkin::get_inverse_binds() { - return GLTFDocument::to_array(inverse_binds); + return GLTFTemplateConvert::to_array(inverse_binds); } void GLTFSkin::set_inverse_binds(Array p_inverse_binds) { - GLTFDocument::set_from_array(inverse_binds, p_inverse_binds); + GLTFTemplateConvert::set_from_array(inverse_binds, p_inverse_binds); } Vector GLTFSkin::get_joints() { @@ -123,41 +124,35 @@ void GLTFSkin::set_skeleton(int p_skeleton) { } Dictionary GLTFSkin::get_joint_i_to_bone_i() { - return GLTFDocument::to_dict(joint_i_to_bone_i); + return GLTFTemplateConvert::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); + GLTFTemplateConvert::set_from_dict(joint_i_to_bone_i, p_joint_i_to_bone_i); } Dictionary GLTFSkin::get_joint_i_to_name() { Dictionary ret; - Map::Element *elem = joint_i_to_name.front(); + HashMap::Iterator elem = joint_i_to_name.begin(); while (elem) { - ret[elem->key()] = String(elem->value()); - elem = elem->next(); + ret[elem->key] = String(elem->value); + ++elem; } return ret; } void GLTFSkin::set_joint_i_to_name(Dictionary p_joint_i_to_name) { - joint_i_to_name = Map(); + joint_i_to_name = HashMap(); Array keys = p_joint_i_to_name.keys(); for (int i = 0; i < keys.size(); i++) { joint_i_to_name[keys[i]] = p_joint_i_to_name[keys[i]]; } } -Ref GLTFSkin::get_pandemonium_skin() { - return pandemonium_skin; +Ref GLTFSkin::get_godot_skin() { + return godot_skin; } -void GLTFSkin::set_pandemonium_skin(Ref p_pandemonium_skin) { - pandemonium_skin = p_pandemonium_skin; -} - -GLTFSkin::GLTFSkin() { -} - -GLTFSkin::~GLTFSkin() { +void GLTFSkin::set_godot_skin(Ref p_godot_skin) { + godot_skin = p_godot_skin; } diff --git a/modules/gltf/gltf_skin.h b/modules/gltf/structures/gltf_skin.h similarity index 92% rename from modules/gltf/gltf_skin.h rename to modules/gltf/structures/gltf_skin.h index 26c17f729..59b6a300a 100644 --- a/modules/gltf/gltf_skin.h +++ b/modules/gltf/structures/gltf_skin.h @@ -1,5 +1,3 @@ -#ifndef GLTF_SKIN_H -#define GLTF_SKIN_H /*************************************************************************/ /* gltf_skin.h */ /*************************************************************************/ @@ -30,11 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "core/resource.h" +#ifndef GLTF_SKIN_H +#define GLTF_SKIN_H -#include "gltf_document.h" - -class Skin; +#include "../gltf_defines.h" +#include "core/io/resource.h" class GLTFSkin : public Resource { GDCLASS(GLTFSkin, Resource); @@ -45,7 +43,7 @@ private: GLTFNodeIndex skin_root = -1; Vector joints_original; - Vector inverse_binds; + Vector inverse_binds; // Note: joints + non_joints should form a complete subtree, or subtrees // with a common parent @@ -66,13 +64,13 @@ private: GLTFSkeletonIndex skeleton = -1; // A mapping from the joint indices (in the order of joints_original) to the - // Pandemonium Skeleton's bone_indices - Map joint_i_to_bone_i; - Map joint_i_to_name; + // Godot Skeleton's bone_indices + HashMap joint_i_to_bone_i; + HashMap 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 pandemonium_skin; + Ref godot_skin; protected: static void _bind_methods(); @@ -105,11 +103,8 @@ public: Dictionary get_joint_i_to_name(); void set_joint_i_to_name(Dictionary p_joint_i_to_name); - Ref get_pandemonium_skin(); - void set_pandemonium_skin(Ref p_pandemonium_skin); - - GLTFSkin(); - ~GLTFSkin(); + Ref get_godot_skin(); + void set_godot_skin(Ref p_godot_skin); }; #endif // GLTF_SKIN_H diff --git a/modules/gltf/gltf_texture.cpp b/modules/gltf/structures/gltf_texture.cpp similarity index 100% rename from modules/gltf/gltf_texture.cpp rename to modules/gltf/structures/gltf_texture.cpp diff --git a/modules/gltf/gltf_texture.h b/modules/gltf/structures/gltf_texture.h similarity index 97% rename from modules/gltf/gltf_texture.h rename to modules/gltf/structures/gltf_texture.h index 08998ba32..b1d12dddf 100644 --- a/modules/gltf/gltf_texture.h +++ b/modules/gltf/structures/gltf_texture.h @@ -1,5 +1,3 @@ -#ifndef GLTF_TEXTURE_H -#define GLTF_TEXTURE_H /*************************************************************************/ /* gltf_texture.h */ /*************************************************************************/ @@ -30,9 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "core/resource.h" +#ifndef GLTF_TEXTURE_H +#define GLTF_TEXTURE_H -#include "gltf_document.h" +#include "../gltf_defines.h" +#include "core/io/resource.h" class GLTFTexture : public Resource { GDCLASS(GLTFTexture, Resource);