summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/classes/AnimatedSprite.xml2
-rw-r--r--doc/classes/AudioEffectInstance.xml15
-rw-r--r--doc/classes/AudioEffectSpectrumAnalyzer.xml23
-rw-r--r--doc/classes/AudioEffectSpectrumAnalyzerInstance.xml31
-rw-r--r--doc/classes/AudioServer.xml12
-rw-r--r--doc/classes/AudioStreamGenerator.xml21
-rw-r--r--doc/classes/AudioStreamGeneratorPlayback.xml57
-rw-r--r--doc/classes/AudioStreamPlaybackResampled.xml15
-rw-r--r--doc/classes/AudioStreamPlayer.xml6
-rw-r--r--doc/classes/AudioStreamPlayer2D.xml6
-rw-r--r--doc/classes/AudioStreamPlayer3D.xml6
-rw-r--r--doc/classes/Basis.xml16
-rw-r--r--doc/classes/CanvasItem.xml2
-rw-r--r--doc/classes/CanvasLayer.xml4
-rw-r--r--doc/classes/EditorFeatureProfile.xml131
-rw-r--r--doc/classes/EditorInterface.xml6
-rw-r--r--doc/classes/File.xml10
-rw-r--r--doc/classes/MainLoop.xml24
-rw-r--r--doc/classes/MeshLibrary.xml18
-rw-r--r--doc/classes/Node.xml28
-rw-r--r--doc/classes/ProjectSettings.xml2
-rw-r--r--doc/classes/ResourceSaver.xml2
-rw-r--r--doc/classes/Spatial.xml2
-rw-r--r--doc/classes/String.xml2
-rw-r--r--doc/classes/StyleBoxFlat.xml6
-rw-r--r--doc/classes/TextureRect.xml12
-rw-r--r--doc/classes/Thread.xml2
-rw-r--r--doc/classes/TileMap.xml2
-rw-r--r--doc/classes/Transform.xml4
-rw-r--r--doc/classes/VisibilityNotifier.xml1
-rw-r--r--doc/classes/VisibilityNotifier2D.xml1
-rw-r--r--doc/classes/VisualShaderNodeBooleanConstant.xml19
-rw-r--r--doc/classes/VisualShaderNodeBooleanUniform.xml15
-rw-r--r--doc/classes/VisualShaderNodeColorFunc.xml23
-rw-r--r--doc/classes/VisualShaderNodeDeterminant.xml15
-rw-r--r--doc/classes/VisualShaderNodeFaceForward.xml15
-rw-r--r--doc/classes/VisualShaderNodeIf.xml15
-rw-r--r--doc/classes/VisualShaderNodeOuterProduct.xml15
-rw-r--r--doc/classes/VisualShaderNodeScalarClamp.xml15
-rw-r--r--doc/classes/VisualShaderNodeScalarDerivativeFunc.xml25
-rw-r--r--doc/classes/VisualShaderNodeScalarFunc.xml22
-rw-r--r--doc/classes/VisualShaderNodeScalarOp.xml2
-rw-r--r--doc/classes/VisualShaderNodeScalarSmoothStep.xml15
-rw-r--r--doc/classes/VisualShaderNodeSwitch.xml15
-rw-r--r--doc/classes/VisualShaderNodeTransformFunc.xml23
-rw-r--r--doc/classes/VisualShaderNodeTransformMult.xml4
-rw-r--r--doc/classes/VisualShaderNodeVectorClamp.xml15
-rw-r--r--doc/classes/VisualShaderNodeVectorDerivativeFunc.xml25
-rw-r--r--doc/classes/VisualShaderNodeVectorDistance.xml15
-rw-r--r--doc/classes/VisualShaderNodeVectorFunc.xml56
-rw-r--r--doc/classes/VisualShaderNodeVectorOp.xml6
-rw-r--r--doc/classes/VisualShaderNodeVectorRefract.xml15
-rw-r--r--doc/classes/VisualShaderNodeVectorScalarSmoothStep.xml15
-rw-r--r--doc/classes/VisualShaderNodeVectorScalarStep.xml15
-rw-r--r--doc/classes/VisualShaderNodeVectorSmoothStep.xml15
-rw-r--r--drivers/gles2/shaders/effect_blur.glsl5
-rw-r--r--drivers/gles3/shaders/effect_blur.glsl5
-rw-r--r--editor/animation_track_editor.cpp197
-rw-r--r--editor/animation_track_editor.h11
-rw-r--r--editor/editor_themes.cpp2
-rw-r--r--editor/icons/icon_auto_key.svg56
-rw-r--r--editor/icons/icon_sprite_sheet.svg61
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp5
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp168
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h3
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.cpp230
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.h17
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp61
-rw-r--r--editor/plugins/visual_shader_editor_plugin.h1
-rw-r--r--editor/scene_tree_dock.cpp68
-rw-r--r--modules/assimp/editor_scene_importer_assimp.cpp2439
-rw-r--r--modules/assimp/editor_scene_importer_assimp.h76
-rw-r--r--modules/mono/csharp_script.cpp29
-rw-r--r--modules/mono/csharp_script.h1
-rw-r--r--modules/opensimplex/doc_classes/OpenSimplexNoise.xml8
-rw-r--r--scene/3d/skeleton.cpp5
-rw-r--r--scene/gui/tree.cpp30
-rw-r--r--scene/register_scene_types.cpp2
-rw-r--r--scene/resources/surface_tool.cpp50
-rw-r--r--scene/resources/surface_tool.h8
-rw-r--r--scene/resources/visual_shader.cpp2
-rw-r--r--scene/resources/visual_shader_nodes.cpp137
-rw-r--r--scene/resources/visual_shader_nodes.h44
83 files changed, 2960 insertions, 1645 deletions
diff --git a/doc/classes/AnimatedSprite.xml b/doc/classes/AnimatedSprite.xml
index 0ee1c75348..555169545e 100644
--- a/doc/classes/AnimatedSprite.xml
+++ b/doc/classes/AnimatedSprite.xml
@@ -23,6 +23,8 @@
</return>
<argument index="0" name="anim" type="String" default="&quot;&quot;">
</argument>
+ <argument index="1" name="backwards" type="bool" default="false">
+ </argument>
<description>
Play the animation set in parameter. If no parameter is provided, the current animation is played. Property [code]backwards[/code] plays the animation in reverse if set to [code]true[/code].
</description>
diff --git a/doc/classes/AudioEffectInstance.xml b/doc/classes/AudioEffectInstance.xml
new file mode 100644
index 0000000000..ed42928eb1
--- /dev/null
+++ b/doc/classes/AudioEffectInstance.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="AudioEffectInstance" inherits="Reference" category="Core" version="3.2">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/AudioEffectSpectrumAnalyzer.xml b/doc/classes/AudioEffectSpectrumAnalyzer.xml
new file mode 100644
index 0000000000..5732039467
--- /dev/null
+++ b/doc/classes/AudioEffectSpectrumAnalyzer.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="AudioEffectSpectrumAnalyzer" inherits="AudioEffect" category="Core" version="3.2">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <members>
+ <member name="buffer_length" type="float" setter="set_buffer_length" getter="get_buffer_length">
+ </member>
+ <member name="fft_size" type="int" setter="set_fft_size" getter="get_fft_size" enum="AudioEffectSpectrumAnalyzer.FFT_Size">
+ </member>
+ <member name="tap_back_pos" type="float" setter="set_tap_back_pos" getter="get_tap_back_pos">
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/AudioEffectSpectrumAnalyzerInstance.xml b/doc/classes/AudioEffectSpectrumAnalyzerInstance.xml
new file mode 100644
index 0000000000..0c0061beb4
--- /dev/null
+++ b/doc/classes/AudioEffectSpectrumAnalyzerInstance.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="AudioEffectSpectrumAnalyzerInstance" inherits="AudioEffectInstance" category="Core" version="3.2">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="get_magnitude_for_frequency_range" qualifiers="const">
+ <return type="Vector2">
+ </return>
+ <argument index="0" name="from_hz" type="float">
+ </argument>
+ <argument index="1" name="to_hz" type="float">
+ </argument>
+ <argument index="2" name="mode" type="int" enum="AudioEffectSpectrumAnalyzerInstance.MagnitudeMode" default="1">
+ </argument>
+ <description>
+ </description>
+ </method>
+ </methods>
+ <constants>
+ <constant name="MAGNITUDE_AVERAGE" value="0" enum="MagnitudeMode">
+ </constant>
+ <constant name="MAGNITUDE_MAX" value="1" enum="MagnitudeMode">
+ </constant>
+ </constants>
+</class>
diff --git a/doc/classes/AudioServer.xml b/doc/classes/AudioServer.xml
index 82e17e4957..32eed54882 100644
--- a/doc/classes/AudioServer.xml
+++ b/doc/classes/AudioServer.xml
@@ -97,6 +97,18 @@
Returns the number of effects on the bus at [code]bus_idx[/code].
</description>
</method>
+ <method name="get_bus_effect_instance">
+ <return type="AudioEffectInstance">
+ </return>
+ <argument index="0" name="bus_idx" type="int">
+ </argument>
+ <argument index="1" name="effect_idx" type="int">
+ </argument>
+ <argument index="2" name="channel" type="int" default="0">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="get_bus_index" qualifiers="const">
<return type="int">
</return>
diff --git a/doc/classes/AudioStreamGenerator.xml b/doc/classes/AudioStreamGenerator.xml
new file mode 100644
index 0000000000..e41b5827b1
--- /dev/null
+++ b/doc/classes/AudioStreamGenerator.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="AudioStreamGenerator" inherits="AudioStream" category="Core" version="3.2">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <members>
+ <member name="buffer_length" type="float" setter="set_buffer_length" getter="get_buffer_length">
+ </member>
+ <member name="mix_rate" type="float" setter="set_mix_rate" getter="get_mix_rate">
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/AudioStreamGeneratorPlayback.xml b/doc/classes/AudioStreamGeneratorPlayback.xml
new file mode 100644
index 0000000000..4b4d622741
--- /dev/null
+++ b/doc/classes/AudioStreamGeneratorPlayback.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="AudioStreamGeneratorPlayback" inherits="AudioStreamPlaybackResampled" category="Core" version="3.2">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="can_push_buffer" qualifiers="const">
+ <return type="bool">
+ </return>
+ <argument index="0" name="amount" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="clear_buffer">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_frames_available" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_skips" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="push_buffer">
+ <return type="bool">
+ </return>
+ <argument index="0" name="frames" type="PoolVector2Array">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="push_frame">
+ <return type="bool">
+ </return>
+ <argument index="0" name="frame" type="Vector2">
+ </argument>
+ <description>
+ </description>
+ </method>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/AudioStreamPlaybackResampled.xml b/doc/classes/AudioStreamPlaybackResampled.xml
new file mode 100644
index 0000000000..764f966167
--- /dev/null
+++ b/doc/classes/AudioStreamPlaybackResampled.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="AudioStreamPlaybackResampled" inherits="AudioStreamPlayback" category="Core" version="3.2">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/AudioStreamPlayer.xml b/doc/classes/AudioStreamPlayer.xml
index d8b87a3c19..1681d796ea 100644
--- a/doc/classes/AudioStreamPlayer.xml
+++ b/doc/classes/AudioStreamPlayer.xml
@@ -19,6 +19,12 @@
Returns the position in the [AudioStream] in seconds.
</description>
</method>
+ <method name="get_stream_playback">
+ <return type="AudioStreamPlayback">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="play">
<return type="void">
</return>
diff --git a/doc/classes/AudioStreamPlayer2D.xml b/doc/classes/AudioStreamPlayer2D.xml
index 1cd2bf7645..e001d11727 100644
--- a/doc/classes/AudioStreamPlayer2D.xml
+++ b/doc/classes/AudioStreamPlayer2D.xml
@@ -19,6 +19,12 @@
Returns the position in the [AudioStream].
</description>
</method>
+ <method name="get_stream_playback">
+ <return type="AudioStreamPlayback">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="play">
<return type="void">
</return>
diff --git a/doc/classes/AudioStreamPlayer3D.xml b/doc/classes/AudioStreamPlayer3D.xml
index 8ff5223e4f..0d973a761a 100644
--- a/doc/classes/AudioStreamPlayer3D.xml
+++ b/doc/classes/AudioStreamPlayer3D.xml
@@ -19,6 +19,12 @@
Returns the position in the [AudioStream].
</description>
</method>
+ <method name="get_stream_playback">
+ <return type="AudioStreamPlayback">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="play">
<return type="void">
</return>
diff --git a/doc/classes/Basis.xml b/doc/classes/Basis.xml
index 7b43d58c71..3710bc3de6 100644
--- a/doc/classes/Basis.xml
+++ b/doc/classes/Basis.xml
@@ -76,6 +76,12 @@
This function considers a discretization of rotations into 24 points on unit sphere, lying along the vectors (x,y,z) with each component being either -1,0 or 1, and returns the index of the point best representing the orientation of the object. It is mainly used by the grid map editor. For further details, refer to Godot source code.
</description>
</method>
+ <method name="get_rotation_quat">
+ <return type="Quat">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="get_scale">
<return type="Vector3">
</return>
@@ -90,6 +96,16 @@
Return the inverse of the matrix.
</description>
</method>
+ <method name="is_equal_approx">
+ <return type="bool">
+ </return>
+ <argument index="0" name="b" type="Basis">
+ </argument>
+ <argument index="1" name="epsilon" type="float" default="0.00001">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="orthonormalized">
<return type="Basis">
</return>
diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml
index c1cf7dcabe..61ccc167a5 100644
--- a/doc/classes/CanvasItem.xml
+++ b/doc/classes/CanvasItem.xml
@@ -575,7 +575,7 @@
<constant name="BLEND_MODE_DISABLED" value="5" enum="BlendMode">
Disable blending mode. Colors including alpha are written as is. Only applicable for render targets with a transparent background. No lighting will be applied.
</constant>
- <constant name="NOTIFICATION_TRANSFORM_CHANGED" value="29">
+ <constant name="NOTIFICATION_TRANSFORM_CHANGED" value="2000">
Canvas item transform has changed. Notification is only received if enabled by [method set_notify_transform] or [method set_notify_local_transform].
</constant>
<constant name="NOTIFICATION_DRAW" value="30">
diff --git a/doc/classes/CanvasLayer.xml b/doc/classes/CanvasLayer.xml
index 3d69c92ea3..04aa7fbbf2 100644
--- a/doc/classes/CanvasLayer.xml
+++ b/doc/classes/CanvasLayer.xml
@@ -25,6 +25,10 @@
<member name="custom_viewport" type="Node" setter="set_custom_viewport" getter="get_custom_viewport">
The custom [Viewport] node assigned to the [CanvasLayer]. If null, uses the default viewport instead.
</member>
+ <member name="follow_viewport_enable" type="bool" setter="set_follow_viewport" getter="is_following_viewport">
+ </member>
+ <member name="follow_viewport_scale" type="float" setter="set_follow_viewport_scale" getter="get_follow_viewport_scale">
+ </member>
<member name="layer" type="int" setter="set_layer" getter="get_layer">
Layer index for draw order. Lower values are drawn first. Default value: [code]1[/code].
</member>
diff --git a/doc/classes/EditorFeatureProfile.xml b/doc/classes/EditorFeatureProfile.xml
new file mode 100644
index 0000000000..e0e56dc009
--- /dev/null
+++ b/doc/classes/EditorFeatureProfile.xml
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="EditorFeatureProfile" inherits="Reference" category="Core" version="3.2">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="get_feature_name">
+ <return type="String">
+ </return>
+ <argument index="0" name="feature" type="int" enum="EditorFeatureProfile.Feature">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="is_class_disabled" qualifiers="const">
+ <return type="bool">
+ </return>
+ <argument index="0" name="class_name" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="is_class_editor_disabled" qualifiers="const">
+ <return type="bool">
+ </return>
+ <argument index="0" name="class_name" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="is_class_property_disabled" qualifiers="const">
+ <return type="bool">
+ </return>
+ <argument index="0" name="class_name" type="String">
+ </argument>
+ <argument index="1" name="arg1" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="is_feature_disabled" qualifiers="const">
+ <return type="bool">
+ </return>
+ <argument index="0" name="feature" type="int" enum="EditorFeatureProfile.Feature">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="load_from_file">
+ <return type="int" enum="Error">
+ </return>
+ <argument index="0" name="path" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="save_to_file">
+ <return type="int" enum="Error">
+ </return>
+ <argument index="0" name="path" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="set_disable_class">
+ <return type="void">
+ </return>
+ <argument index="0" name="class_name" type="String">
+ </argument>
+ <argument index="1" name="disable" type="bool">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="set_disable_class_editor">
+ <return type="void">
+ </return>
+ <argument index="0" name="class_name" type="String">
+ </argument>
+ <argument index="1" name="disable" type="bool">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="set_disable_class_property">
+ <return type="void">
+ </return>
+ <argument index="0" name="class_name" type="String">
+ </argument>
+ <argument index="1" name="property" type="String">
+ </argument>
+ <argument index="2" name="arg2" type="bool">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="set_disable_feature">
+ <return type="void">
+ </return>
+ <argument index="0" name="feature" type="int" enum="EditorFeatureProfile.Feature">
+ </argument>
+ <argument index="1" name="disable" type="bool">
+ </argument>
+ <description>
+ </description>
+ </method>
+ </methods>
+ <constants>
+ <constant name="FEATURE_3D" value="0" enum="Feature">
+ </constant>
+ <constant name="FEATURE_SCRIPT" value="1" enum="Feature">
+ </constant>
+ <constant name="FEATURE_ASSET_LIB" value="2" enum="Feature">
+ </constant>
+ <constant name="FEATURE_SCENE_TREE" value="3" enum="Feature">
+ </constant>
+ <constant name="FEATURE_IMPORT_DOCK" value="4" enum="Feature">
+ </constant>
+ <constant name="FEATURE_NODE_DOCK" value="5" enum="Feature">
+ </constant>
+ <constant name="FEATURE_FILESYSTEM_DOCK" value="6" enum="Feature">
+ </constant>
+ <constant name="FEATURE_MAX" value="7" enum="Feature">
+ </constant>
+ </constants>
+</class>
diff --git a/doc/classes/EditorInterface.xml b/doc/classes/EditorInterface.xml
index 30ff609a81..2dc8d716a7 100644
--- a/doc/classes/EditorInterface.xml
+++ b/doc/classes/EditorInterface.xml
@@ -48,6 +48,12 @@
Returns the editor [Viewport].
</description>
</method>
+ <method name="get_inspector" qualifiers="const">
+ <return type="EditorInspector">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="get_open_scenes" qualifiers="const">
<return type="Array">
</return>
diff --git a/doc/classes/File.xml b/doc/classes/File.xml
index 8a785ab263..79462816e3 100644
--- a/doc/classes/File.xml
+++ b/doc/classes/File.xml
@@ -227,7 +227,7 @@
</return>
<argument index="0" name="path" type="String">
</argument>
- <argument index="1" name="flags" type="int">
+ <argument index="1" name="flags" type="int" enum="File.ModeFlags">
</argument>
<description>
Opens the file for writing or reading, depending on the flags.
@@ -238,9 +238,9 @@
</return>
<argument index="0" name="path" type="String">
</argument>
- <argument index="1" name="mode_flags" type="int">
+ <argument index="1" name="mode_flags" type="int" enum="File.ModeFlags">
</argument>
- <argument index="2" name="compression_mode" type="int" default="0">
+ <argument index="2" name="compression_mode" type="int" enum="File.CompressionMode" default="0">
</argument>
<description>
Opens a compressed file for reading or writing. Use COMPRESSION_* constants to set [code]compression_mode[/code].
@@ -251,7 +251,7 @@
</return>
<argument index="0" name="path" type="String">
</argument>
- <argument index="1" name="mode_flags" type="int">
+ <argument index="1" name="mode_flags" type="int" enum="File.ModeFlags">
</argument>
<argument index="2" name="key" type="PoolByteArray">
</argument>
@@ -264,7 +264,7 @@
</return>
<argument index="0" name="path" type="String">
</argument>
- <argument index="1" name="mode_flags" type="int">
+ <argument index="1" name="mode_flags" type="int" enum="File.ModeFlags">
</argument>
<argument index="2" name="pass" type="String">
</argument>
diff --git a/doc/classes/MainLoop.xml b/doc/classes/MainLoop.xml
index 8d8d4c46cc..6b7d9cc834 100644
--- a/doc/classes/MainLoop.xml
+++ b/doc/classes/MainLoop.xml
@@ -114,29 +114,29 @@
</method>
</methods>
<constants>
- <constant name="NOTIFICATION_WM_MOUSE_ENTER" value="2">
+ <constant name="NOTIFICATION_WM_MOUSE_ENTER" value="1002">
</constant>
- <constant name="NOTIFICATION_WM_MOUSE_EXIT" value="3">
+ <constant name="NOTIFICATION_WM_MOUSE_EXIT" value="1003">
</constant>
- <constant name="NOTIFICATION_WM_FOCUS_IN" value="4">
+ <constant name="NOTIFICATION_WM_FOCUS_IN" value="1004">
</constant>
- <constant name="NOTIFICATION_WM_FOCUS_OUT" value="5">
+ <constant name="NOTIFICATION_WM_FOCUS_OUT" value="1005">
</constant>
- <constant name="NOTIFICATION_WM_QUIT_REQUEST" value="6">
+ <constant name="NOTIFICATION_WM_QUIT_REQUEST" value="1006">
</constant>
- <constant name="NOTIFICATION_WM_GO_BACK_REQUEST" value="7">
+ <constant name="NOTIFICATION_WM_GO_BACK_REQUEST" value="1007">
</constant>
- <constant name="NOTIFICATION_WM_UNFOCUS_REQUEST" value="8">
+ <constant name="NOTIFICATION_WM_UNFOCUS_REQUEST" value="1008">
</constant>
- <constant name="NOTIFICATION_OS_MEMORY_WARNING" value="9">
+ <constant name="NOTIFICATION_OS_MEMORY_WARNING" value="1009">
</constant>
- <constant name="NOTIFICATION_TRANSLATION_CHANGED" value="90">
+ <constant name="NOTIFICATION_TRANSLATION_CHANGED" value="1010">
</constant>
- <constant name="NOTIFICATION_WM_ABOUT" value="91">
+ <constant name="NOTIFICATION_WM_ABOUT" value="1011">
</constant>
- <constant name="NOTIFICATION_CRASH" value="92">
+ <constant name="NOTIFICATION_CRASH" value="1012">
</constant>
- <constant name="NOTIFICATION_OS_IME_UPDATE" value="93">
+ <constant name="NOTIFICATION_OS_IME_UPDATE" value="1013">
</constant>
</constants>
</class>
diff --git a/doc/classes/MeshLibrary.xml b/doc/classes/MeshLibrary.xml
index 8fb10696f2..02d87a9c48 100644
--- a/doc/classes/MeshLibrary.xml
+++ b/doc/classes/MeshLibrary.xml
@@ -68,6 +68,14 @@
<description>
</description>
</method>
+ <method name="get_item_navmesh_transform" qualifiers="const">
+ <return type="Transform">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="get_item_preview" qualifiers="const">
<return type="Texture">
</return>
@@ -132,6 +140,16 @@
<description>
</description>
</method>
+ <method name="set_item_navmesh_transform">
+ <return type="void">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <argument index="1" name="navmesh" type="Transform">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="set_item_preview">
<return type="void">
</return>
diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml
index bf32d4041a..646b921743 100644
--- a/doc/classes/Node.xml
+++ b/doc/classes/Node.xml
@@ -886,15 +886,37 @@
<constant name="NOTIFICATION_PATH_CHANGED" value="23">
Notification received when the node's [NodePath] changed.
</constant>
- <constant name="NOTIFICATION_TRANSLATION_CHANGED" value="24">
- Notification received when translations may have changed. Can be triggered by the user changing the locale. Can be used to respond to language changes, for example to change the UI strings on the fly. Useful when working with the built-in translation support, like [method Object.tr].
- </constant>
<constant name="NOTIFICATION_INTERNAL_PROCESS" value="25">
Notification received every frame when the internal process flag is set (see [method set_process_internal]).
</constant>
<constant name="NOTIFICATION_INTERNAL_PHYSICS_PROCESS" value="26">
Notification received every frame when the internal physics process flag is set (see [method set_physics_process_internal]).
</constant>
+ <constant name="NOTIFICATION_WM_MOUSE_ENTER" value="1002">
+ </constant>
+ <constant name="NOTIFICATION_WM_MOUSE_EXIT" value="1003">
+ </constant>
+ <constant name="NOTIFICATION_WM_FOCUS_IN" value="1004">
+ </constant>
+ <constant name="NOTIFICATION_WM_FOCUS_OUT" value="1005">
+ </constant>
+ <constant name="NOTIFICATION_WM_QUIT_REQUEST" value="1006">
+ </constant>
+ <constant name="NOTIFICATION_WM_GO_BACK_REQUEST" value="1007">
+ </constant>
+ <constant name="NOTIFICATION_WM_UNFOCUS_REQUEST" value="1008">
+ </constant>
+ <constant name="NOTIFICATION_OS_MEMORY_WARNING" value="1009">
+ </constant>
+ <constant name="NOTIFICATION_TRANSLATION_CHANGED" value="1010">
+ Notification received when translations may have changed. Can be triggered by the user changing the locale. Can be used to respond to language changes, for example to change the UI strings on the fly. Useful when working with the built-in translation support, like [method Object.tr].
+ </constant>
+ <constant name="NOTIFICATION_WM_ABOUT" value="1011">
+ </constant>
+ <constant name="NOTIFICATION_CRASH" value="1012">
+ </constant>
+ <constant name="NOTIFICATION_OS_IME_UPDATE" value="1013">
+ </constant>
<constant name="PAUSE_MODE_INHERIT" value="0" enum="PauseMode">
Inherits pause mode from the node's parent. For the root node, it is equivalent to PAUSE_MODE_STOP. Default.
</constant>
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index e67e3c9a03..ee3f3260d7 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -215,6 +215,8 @@
<member name="audio/channel_disable_time" type="float" setter="" getter="">
Audio buses will disable automatically when sound goes below a given DB threshold for a given time. This saves CPU as effects assigned to that bus will no longer do any processing.
</member>
+ <member name="audio/default_bus_layout" type="String" setter="" getter="">
+ </member>
<member name="audio/driver" type="String" setter="" getter="">
</member>
<member name="audio/enable_audio_input" type="bool" setter="" getter="">
diff --git a/doc/classes/ResourceSaver.xml b/doc/classes/ResourceSaver.xml
index 165ab0dc95..079d1388f0 100644
--- a/doc/classes/ResourceSaver.xml
+++ b/doc/classes/ResourceSaver.xml
@@ -27,7 +27,7 @@
</argument>
<argument index="1" name="resource" type="Resource">
</argument>
- <argument index="2" name="flags" type="int" default="0">
+ <argument index="2" name="flags" type="int" enum="ResourceSaver.SaverFlags" default="0">
</argument>
<description>
Saves a resource to disk.
diff --git a/doc/classes/Spatial.xml b/doc/classes/Spatial.xml
index 7e42e06960..f28ddcf351 100644
--- a/doc/classes/Spatial.xml
+++ b/doc/classes/Spatial.xml
@@ -329,7 +329,7 @@
</signal>
</signals>
<constants>
- <constant name="NOTIFICATION_TRANSFORM_CHANGED" value="29">
+ <constant name="NOTIFICATION_TRANSFORM_CHANGED" value="2000">
Spatial nodes receives this notification when their global transform changes. This means that either the current or a parent node changed its transform.
In order for [code]NOTIFICATION_TRANSFORM_CHANGED[/code] to work, users first need to ask for it, with [method set_notify_transform].
</constant>
diff --git a/doc/classes/String.xml b/doc/classes/String.xml
index 0cfa246275..67a9b80602 100644
--- a/doc/classes/String.xml
+++ b/doc/classes/String.xml
@@ -437,7 +437,7 @@
</return>
<description>
Returns [code]true[/code] if this string is free from characters that aren't allowed in file names, those being:
- [code]: / \ ? * " | % < >[/code]
+ [code]: / \ ? * " | % &lt; &gt;[/code]
</description>
</method>
<method name="is_valid_float">
diff --git a/doc/classes/StyleBoxFlat.xml b/doc/classes/StyleBoxFlat.xml
index d74f481f55..34f99755a4 100644
--- a/doc/classes/StyleBoxFlat.xml
+++ b/doc/classes/StyleBoxFlat.xml
@@ -148,12 +148,12 @@
<member name="shadow_color" type="Color" setter="set_shadow_color" getter="get_shadow_color">
The color of the shadow. (This has no effect when shadow_size &lt; 1)
</member>
- <member name="shadow_size" type="int" setter="set_shadow_size" getter="get_shadow_size">
- The shadow size in pixels.
- </member>
<member name="shadow_offset" type="Vector2" setter="set_shadow_offset" getter="get_shadow_offset">
The shadow offset in pixels. Adjusts the position of the shadow relatively to the stylebox.
</member>
+ <member name="shadow_size" type="int" setter="set_shadow_size" getter="get_shadow_size">
+ The shadow size in pixels.
+ </member>
</members>
<constants>
</constants>
diff --git a/doc/classes/TextureRect.xml b/doc/classes/TextureRect.xml
index 7f26fe18e5..eb8014bab8 100644
--- a/doc/classes/TextureRect.xml
+++ b/doc/classes/TextureRect.xml
@@ -16,18 +16,18 @@
<member name="expand" type="bool" setter="set_expand" getter="has_expand">
If [code]true[/code], the texture scales to fit its bounding rectangle. Default value: [code]false[/code].
</member>
- <member name="stretch_mode" type="int" setter="set_stretch_mode" getter="get_stretch_mode" enum="TextureRect.StretchMode">
- Controls the texture's behavior when resizing the node's bounding rectangle. See [enum StretchMode].
- </member>
- <member name="texture" type="Texture" setter="set_texture" getter="get_texture">
- The node's [Texture] resource.
- </member>
<member name="flip_h" type="bool" setter="set_flip_h" getter="is_flipped_h">
If [code]true[/code], texture is flipped horizontally. Default value: [code]false[/code].
</member>
<member name="flip_v" type="bool" setter="set_flip_v" getter="is_flipped_v">
If [code]true[/code], texture is flipped vertically. Default value: [code]false[/code].
</member>
+ <member name="stretch_mode" type="int" setter="set_stretch_mode" getter="get_stretch_mode" enum="TextureRect.StretchMode">
+ Controls the texture's behavior when resizing the node's bounding rectangle. See [enum StretchMode].
+ </member>
+ <member name="texture" type="Texture" setter="set_texture" getter="get_texture">
+ The node's [Texture] resource.
+ </member>
</members>
<constants>
<constant name="STRETCH_SCALE_ON_EXPAND" value="0" enum="StretchMode">
diff --git a/doc/classes/Thread.xml b/doc/classes/Thread.xml
index d2aa1a3f03..ea558735aa 100644
--- a/doc/classes/Thread.xml
+++ b/doc/classes/Thread.xml
@@ -34,7 +34,7 @@
</argument>
<argument index="2" name="userdata" type="Variant" default="null">
</argument>
- <argument index="3" name="priority" type="int" default="1">
+ <argument index="3" name="priority" type="int" enum="Thread.Priority" default="1">
</argument>
<description>
Starts a new [Thread] that runs "method" on object "instance" with "userdata" passed as an argument. The "priority" of the [Thread] can be changed by passing a PRIORITY_* enum.
diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml
index 3c047487e0..54d9e05180 100644
--- a/doc/classes/TileMap.xml
+++ b/doc/classes/TileMap.xml
@@ -171,7 +171,7 @@
# Write your custom logic here.
# To call the default method:
.set_cell(x, y, tile, flip_x, flip_y, transpose, autotile_coord)
- [/codeblock]
+ [/codeblock]
</description>
</method>
<method name="set_cellv">
diff --git a/doc/classes/Transform.xml b/doc/classes/Transform.xml
index e110fa7b40..8c9a1a36b3 100644
--- a/doc/classes/Transform.xml
+++ b/doc/classes/Transform.xml
@@ -172,9 +172,9 @@
</constant>
<constant name="FLIP_X" value="Transform( -1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 )">
</constant>
- <constant name="FLIP_Y" value="Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 )">
+ <constant name="FLIP_Y" value="Transform( 1, 0, 0, 0, -1, 0, 0, 0, 1, 0, 0, 0 )">
</constant>
- <constant name="FLIP_Z" value="Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 )">
+ <constant name="FLIP_Z" value="Transform( 1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0 )">
</constant>
</constants>
</class>
diff --git a/doc/classes/VisibilityNotifier.xml b/doc/classes/VisibilityNotifier.xml
index e30078d82f..062371ba29 100644
--- a/doc/classes/VisibilityNotifier.xml
+++ b/doc/classes/VisibilityNotifier.xml
@@ -16,6 +16,7 @@
</return>
<description>
If [code]true[/code], the bounding box is on the screen.
+ Note: It takes one frame for the node's visibility to be assessed once added to the scene tree, so this method will return [code]false[/code] right after it is instantiated, even if it will be on screen in the draw pass.
</description>
</method>
</methods>
diff --git a/doc/classes/VisibilityNotifier2D.xml b/doc/classes/VisibilityNotifier2D.xml
index 5689ced4bf..7b553c7518 100644
--- a/doc/classes/VisibilityNotifier2D.xml
+++ b/doc/classes/VisibilityNotifier2D.xml
@@ -16,6 +16,7 @@
</return>
<description>
If [code]true[/code], the bounding rectangle is on the screen.
+ Note: It takes one frame for the node's visibility to be assessed once added to the scene tree, so this method will return [code]false[/code] right after it is instantiated, even if it will be on screen in the draw pass.
</description>
</method>
</methods>
diff --git a/doc/classes/VisualShaderNodeBooleanConstant.xml b/doc/classes/VisualShaderNodeBooleanConstant.xml
new file mode 100644
index 0000000000..dc2abfff25
--- /dev/null
+++ b/doc/classes/VisualShaderNodeBooleanConstant.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeBooleanConstant" inherits="VisualShaderNode" category="Core" version="3.2">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <members>
+ <member name="constant" type="bool" setter="set_constant" getter="get_constant">
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/VisualShaderNodeBooleanUniform.xml b/doc/classes/VisualShaderNodeBooleanUniform.xml
new file mode 100644
index 0000000000..06dca13426
--- /dev/null
+++ b/doc/classes/VisualShaderNodeBooleanUniform.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeBooleanUniform" inherits="VisualShaderNodeUniform" category="Core" version="3.2">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/VisualShaderNodeColorFunc.xml b/doc/classes/VisualShaderNodeColorFunc.xml
new file mode 100644
index 0000000000..e906049cae
--- /dev/null
+++ b/doc/classes/VisualShaderNodeColorFunc.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeColorFunc" inherits="VisualShaderNode" category="Core" version="3.2">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <members>
+ <member name="function" type="int" setter="set_function" getter="get_function" enum="VisualShaderNodeColorFunc.Function">
+ </member>
+ </members>
+ <constants>
+ <constant name="FUNC_GRAYSCALE" value="0" enum="Function">
+ </constant>
+ <constant name="FUNC_SEPIA" value="1" enum="Function">
+ </constant>
+ </constants>
+</class>
diff --git a/doc/classes/VisualShaderNodeDeterminant.xml b/doc/classes/VisualShaderNodeDeterminant.xml
new file mode 100644
index 0000000000..661894e7b6
--- /dev/null
+++ b/doc/classes/VisualShaderNodeDeterminant.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeDeterminant" inherits="VisualShaderNode" category="Core" version="3.2">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/VisualShaderNodeFaceForward.xml b/doc/classes/VisualShaderNodeFaceForward.xml
new file mode 100644
index 0000000000..823e251203
--- /dev/null
+++ b/doc/classes/VisualShaderNodeFaceForward.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeFaceForward" inherits="VisualShaderNode" category="Core" version="3.2">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/VisualShaderNodeIf.xml b/doc/classes/VisualShaderNodeIf.xml
new file mode 100644
index 0000000000..4e606b95aa
--- /dev/null
+++ b/doc/classes/VisualShaderNodeIf.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeIf" inherits="VisualShaderNode" category="Core" version="3.2">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/VisualShaderNodeOuterProduct.xml b/doc/classes/VisualShaderNodeOuterProduct.xml
new file mode 100644
index 0000000000..4ea4713428
--- /dev/null
+++ b/doc/classes/VisualShaderNodeOuterProduct.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeOuterProduct" inherits="VisualShaderNode" category="Core" version="3.2">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/VisualShaderNodeScalarClamp.xml b/doc/classes/VisualShaderNodeScalarClamp.xml
new file mode 100644
index 0000000000..66401c6bf6
--- /dev/null
+++ b/doc/classes/VisualShaderNodeScalarClamp.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeScalarClamp" inherits="VisualShaderNode" category="Core" version="3.2">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/VisualShaderNodeScalarDerivativeFunc.xml b/doc/classes/VisualShaderNodeScalarDerivativeFunc.xml
new file mode 100644
index 0000000000..c78be81034
--- /dev/null
+++ b/doc/classes/VisualShaderNodeScalarDerivativeFunc.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeScalarDerivativeFunc" inherits="VisualShaderNode" category="Core" version="3.2">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <members>
+ <member name="function" type="int" setter="set_function" getter="get_function" enum="VisualShaderNodeScalarDerivativeFunc.Function">
+ </member>
+ </members>
+ <constants>
+ <constant name="FUNC_SUM" value="0" enum="Function">
+ </constant>
+ <constant name="FUNC_X" value="1" enum="Function">
+ </constant>
+ <constant name="FUNC_Y" value="2" enum="Function">
+ </constant>
+ </constants>
+</class>
diff --git a/doc/classes/VisualShaderNodeScalarFunc.xml b/doc/classes/VisualShaderNodeScalarFunc.xml
index 67a5d3f5c4..66bdfab5a6 100644
--- a/doc/classes/VisualShaderNodeScalarFunc.xml
+++ b/doc/classes/VisualShaderNodeScalarFunc.xml
@@ -55,5 +55,27 @@
</constant>
<constant name="FUNC_NEGATE" value="19" enum="Function">
</constant>
+ <constant name="FUNC_ACOSH" value="20" enum="Function">
+ </constant>
+ <constant name="FUNC_ASINH" value="21" enum="Function">
+ </constant>
+ <constant name="FUNC_ATANH" value="22" enum="Function">
+ </constant>
+ <constant name="FUNC_DEGREES" value="23" enum="Function">
+ </constant>
+ <constant name="FUNC_EXP2" value="24" enum="Function">
+ </constant>
+ <constant name="FUNC_INVERSE_SQRT" value="25" enum="Function">
+ </constant>
+ <constant name="FUNC_LOG2" value="26" enum="Function">
+ </constant>
+ <constant name="FUNC_RADIANS" value="27" enum="Function">
+ </constant>
+ <constant name="FUNC_RECIPROCAL" value="28" enum="Function">
+ </constant>
+ <constant name="FUNC_ROUNDEVEN" value="29" enum="Function">
+ </constant>
+ <constant name="FUNC_TRUNC" value="30" enum="Function">
+ </constant>
</constants>
</class>
diff --git a/doc/classes/VisualShaderNodeScalarOp.xml b/doc/classes/VisualShaderNodeScalarOp.xml
index 34ec89f8be..c5a331679f 100644
--- a/doc/classes/VisualShaderNodeScalarOp.xml
+++ b/doc/classes/VisualShaderNodeScalarOp.xml
@@ -33,5 +33,7 @@
</constant>
<constant name="OP_ATAN2" value="8" enum="Operator">
</constant>
+ <constant name="OP_STEP" value="9" enum="Operator">
+ </constant>
</constants>
</class>
diff --git a/doc/classes/VisualShaderNodeScalarSmoothStep.xml b/doc/classes/VisualShaderNodeScalarSmoothStep.xml
new file mode 100644
index 0000000000..5be54d0b4f
--- /dev/null
+++ b/doc/classes/VisualShaderNodeScalarSmoothStep.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeScalarSmoothStep" inherits="VisualShaderNode" category="Core" version="3.2">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/VisualShaderNodeSwitch.xml b/doc/classes/VisualShaderNodeSwitch.xml
new file mode 100644
index 0000000000..1c346e7a1f
--- /dev/null
+++ b/doc/classes/VisualShaderNodeSwitch.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeSwitch" inherits="VisualShaderNode" category="Core" version="3.2">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/VisualShaderNodeTransformFunc.xml b/doc/classes/VisualShaderNodeTransformFunc.xml
new file mode 100644
index 0000000000..601e66ca16
--- /dev/null
+++ b/doc/classes/VisualShaderNodeTransformFunc.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeTransformFunc" inherits="VisualShaderNode" category="Core" version="3.2">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <members>
+ <member name="function" type="int" setter="set_function" getter="get_function" enum="VisualShaderNodeTransformFunc.Function">
+ </member>
+ </members>
+ <constants>
+ <constant name="FUNC_INVERSE" value="0" enum="Function">
+ </constant>
+ <constant name="FUNC_TRANSPOSE" value="1" enum="Function">
+ </constant>
+ </constants>
+</class>
diff --git a/doc/classes/VisualShaderNodeTransformMult.xml b/doc/classes/VisualShaderNodeTransformMult.xml
index 47026b400e..e9c445526d 100644
--- a/doc/classes/VisualShaderNodeTransformMult.xml
+++ b/doc/classes/VisualShaderNodeTransformMult.xml
@@ -19,5 +19,9 @@
</constant>
<constant name="OP_BxA" value="1" enum="Operator">
</constant>
+ <constant name="OP_AxB_COMP" value="2" enum="Operator">
+ </constant>
+ <constant name="OP_BxA_COMP" value="3" enum="Operator">
+ </constant>
</constants>
</class>
diff --git a/doc/classes/VisualShaderNodeVectorClamp.xml b/doc/classes/VisualShaderNodeVectorClamp.xml
new file mode 100644
index 0000000000..677625ebce
--- /dev/null
+++ b/doc/classes/VisualShaderNodeVectorClamp.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeVectorClamp" inherits="VisualShaderNode" category="Core" version="3.2">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/VisualShaderNodeVectorDerivativeFunc.xml b/doc/classes/VisualShaderNodeVectorDerivativeFunc.xml
new file mode 100644
index 0000000000..e382cb0b58
--- /dev/null
+++ b/doc/classes/VisualShaderNodeVectorDerivativeFunc.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeVectorDerivativeFunc" inherits="VisualShaderNode" category="Core" version="3.2">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <members>
+ <member name="function" type="int" setter="set_function" getter="get_function" enum="VisualShaderNodeVectorDerivativeFunc.Function">
+ </member>
+ </members>
+ <constants>
+ <constant name="FUNC_SUM" value="0" enum="Function">
+ </constant>
+ <constant name="FUNC_X" value="1" enum="Function">
+ </constant>
+ <constant name="FUNC_Y" value="2" enum="Function">
+ </constant>
+ </constants>
+</class>
diff --git a/doc/classes/VisualShaderNodeVectorDistance.xml b/doc/classes/VisualShaderNodeVectorDistance.xml
new file mode 100644
index 0000000000..d83a520a46
--- /dev/null
+++ b/doc/classes/VisualShaderNodeVectorDistance.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeVectorDistance" inherits="VisualShaderNode" category="Core" version="3.2">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/VisualShaderNodeVectorFunc.xml b/doc/classes/VisualShaderNodeVectorFunc.xml
index 968d73d4e1..5e57d85b4c 100644
--- a/doc/classes/VisualShaderNodeVectorFunc.xml
+++ b/doc/classes/VisualShaderNodeVectorFunc.xml
@@ -27,5 +27,61 @@
</constant>
<constant name="FUNC_HSV2RGB" value="5" enum="Function">
</constant>
+ <constant name="FUNC_ABS" value="6" enum="Function">
+ </constant>
+ <constant name="FUNC_ACOS" value="7" enum="Function">
+ </constant>
+ <constant name="FUNC_ACOSH" value="8" enum="Function">
+ </constant>
+ <constant name="FUNC_ASIN" value="9" enum="Function">
+ </constant>
+ <constant name="FUNC_ASINH" value="10" enum="Function">
+ </constant>
+ <constant name="FUNC_ATAN" value="11" enum="Function">
+ </constant>
+ <constant name="FUNC_ATANH" value="12" enum="Function">
+ </constant>
+ <constant name="FUNC_CEIL" value="13" enum="Function">
+ </constant>
+ <constant name="FUNC_COS" value="14" enum="Function">
+ </constant>
+ <constant name="FUNC_COSH" value="15" enum="Function">
+ </constant>
+ <constant name="FUNC_DEGREES" value="16" enum="Function">
+ </constant>
+ <constant name="FUNC_EXP" value="17" enum="Function">
+ </constant>
+ <constant name="FUNC_EXP2" value="18" enum="Function">
+ </constant>
+ <constant name="FUNC_FLOOR" value="19" enum="Function">
+ </constant>
+ <constant name="FUNC_FRAC" value="20" enum="Function">
+ </constant>
+ <constant name="FUNC_INVERSE_SQRT" value="21" enum="Function">
+ </constant>
+ <constant name="FUNC_LOG" value="22" enum="Function">
+ </constant>
+ <constant name="FUNC_LOG2" value="23" enum="Function">
+ </constant>
+ <constant name="FUNC_RADIANS" value="24" enum="Function">
+ </constant>
+ <constant name="FUNC_ROUND" value="25" enum="Function">
+ </constant>
+ <constant name="FUNC_ROUNDEVEN" value="26" enum="Function">
+ </constant>
+ <constant name="FUNC_SIGN" value="27" enum="Function">
+ </constant>
+ <constant name="FUNC_SIN" value="28" enum="Function">
+ </constant>
+ <constant name="FUNC_SINH" value="29" enum="Function">
+ </constant>
+ <constant name="FUNC_SQRT" value="30" enum="Function">
+ </constant>
+ <constant name="FUNC_TAN" value="31" enum="Function">
+ </constant>
+ <constant name="FUNC_TANH" value="32" enum="Function">
+ </constant>
+ <constant name="FUNC_TRUNC" value="33" enum="Function">
+ </constant>
</constants>
</class>
diff --git a/doc/classes/VisualShaderNodeVectorOp.xml b/doc/classes/VisualShaderNodeVectorOp.xml
index 16c1f8f25d..6524f129da 100644
--- a/doc/classes/VisualShaderNodeVectorOp.xml
+++ b/doc/classes/VisualShaderNodeVectorOp.xml
@@ -33,5 +33,11 @@
</constant>
<constant name="OP_CROSS" value="8" enum="Operator">
</constant>
+ <constant name="OP_ATAN2" value="9" enum="Operator">
+ </constant>
+ <constant name="OP_REFLECT" value="10" enum="Operator">
+ </constant>
+ <constant name="OP_STEP" value="11" enum="Operator">
+ </constant>
</constants>
</class>
diff --git a/doc/classes/VisualShaderNodeVectorRefract.xml b/doc/classes/VisualShaderNodeVectorRefract.xml
new file mode 100644
index 0000000000..32bb0692d4
--- /dev/null
+++ b/doc/classes/VisualShaderNodeVectorRefract.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeVectorRefract" inherits="VisualShaderNode" category="Core" version="3.2">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/VisualShaderNodeVectorScalarSmoothStep.xml b/doc/classes/VisualShaderNodeVectorScalarSmoothStep.xml
new file mode 100644
index 0000000000..a158b4731d
--- /dev/null
+++ b/doc/classes/VisualShaderNodeVectorScalarSmoothStep.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeVectorScalarSmoothStep" inherits="VisualShaderNode" category="Core" version="3.2">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/VisualShaderNodeVectorScalarStep.xml b/doc/classes/VisualShaderNodeVectorScalarStep.xml
new file mode 100644
index 0000000000..88c9b006fc
--- /dev/null
+++ b/doc/classes/VisualShaderNodeVectorScalarStep.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeVectorScalarStep" inherits="VisualShaderNode" category="Core" version="3.2">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/VisualShaderNodeVectorSmoothStep.xml b/doc/classes/VisualShaderNodeVectorSmoothStep.xml
new file mode 100644
index 0000000000..1ddd5f26ce
--- /dev/null
+++ b/doc/classes/VisualShaderNodeVectorSmoothStep.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeVectorSmoothStep" inherits="VisualShaderNode" category="Core" version="3.2">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/drivers/gles2/shaders/effect_blur.glsl b/drivers/gles2/shaders/effect_blur.glsl
index a531802c75..df79e89931 100644
--- a/drivers/gles2/shaders/effect_blur.glsl
+++ b/drivers/gles2/shaders/effect_blur.glsl
@@ -116,12 +116,13 @@ void main() {
#ifdef GAUSSIAN_HORIZONTAL
vec2 pix_size = pixel_size;
pix_size *= 0.5; //reading from larger buffer, so use more samples
+ // sigma 2
vec4 color = textureLod(source_color, uv_interp + vec2(0.0, 0.0) * pix_size, lod) * 0.214607;
color += textureLod(source_color, uv_interp + vec2(1.0, 0.0) * pix_size, lod) * 0.189879;
- color += textureLod(source_color, uv_interp + vec2(2.0, 0.0) * pix_size, lod) * 0.157305;
+ color += textureLod(source_color, uv_interp + vec2(2.0, 0.0) * pix_size, lod) * 0.131514;
color += textureLod(source_color, uv_interp + vec2(3.0, 0.0) * pix_size, lod) * 0.071303;
color += textureLod(source_color, uv_interp + vec2(-1.0, 0.0) * pix_size, lod) * 0.189879;
- color += textureLod(source_color, uv_interp + vec2(-2.0, 0.0) * pix_size, lod) * 0.157305;
+ color += textureLod(source_color, uv_interp + vec2(-2.0, 0.0) * pix_size, lod) * 0.131514;
color += textureLod(source_color, uv_interp + vec2(-3.0, 0.0) * pix_size, lod) * 0.071303;
frag_color = color;
#endif
diff --git a/drivers/gles3/shaders/effect_blur.glsl b/drivers/gles3/shaders/effect_blur.glsl
index fc15ca31b1..ff5a9f326f 100644
--- a/drivers/gles3/shaders/effect_blur.glsl
+++ b/drivers/gles3/shaders/effect_blur.glsl
@@ -117,12 +117,13 @@ void main() {
#ifdef GAUSSIAN_HORIZONTAL
vec2 pix_size = pixel_size;
pix_size *= 0.5; //reading from larger buffer, so use more samples
+ // sigma 2
vec4 color = textureLod(source_color, uv_interp + vec2(0.0, 0.0) * pix_size, lod) * 0.214607;
color += textureLod(source_color, uv_interp + vec2(1.0, 0.0) * pix_size, lod) * 0.189879;
- color += textureLod(source_color, uv_interp + vec2(2.0, 0.0) * pix_size, lod) * 0.157305;
+ color += textureLod(source_color, uv_interp + vec2(2.0, 0.0) * pix_size, lod) * 0.131514;
color += textureLod(source_color, uv_interp + vec2(3.0, 0.0) * pix_size, lod) * 0.071303;
color += textureLod(source_color, uv_interp + vec2(-1.0, 0.0) * pix_size, lod) * 0.189879;
- color += textureLod(source_color, uv_interp + vec2(-2.0, 0.0) * pix_size, lod) * 0.157305;
+ color += textureLod(source_color, uv_interp + vec2(-2.0, 0.0) * pix_size, lod) * 0.131514;
color += textureLod(source_color, uv_interp + vec2(-3.0, 0.0) * pix_size, lod) * 0.071303;
frag_color = color;
#endif
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index 8807a01f64..2307d340d8 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -109,9 +109,17 @@ public:
ERR_FAIL_COND_V(key == -1, false);
String name = p_name;
- if (name == "time") {
+ if (name == "time" || name == "frame") {
float new_time = p_value;
+
+ if (name == "frame") {
+ float fps = animation->get_step();
+ if (fps > 0) {
+ fps = 1.0 / fps;
+ }
+ new_time /= fps;
+ }
if (new_time == key_ofs)
return true;
@@ -413,6 +421,13 @@ public:
if (name == "time") {
r_ret = key_ofs;
return true;
+ } else if (name == "frame") {
+ float fps = animation->get_step();
+ if (fps > 0) {
+ fps = 1.0 / fps;
+ }
+ r_ret = key_ofs * fps;
+ return true;
} else if (name == "easing") {
r_ret = animation->track_get_key_transition(track, key);
return true;
@@ -527,7 +542,12 @@ public:
int key = animation->track_find_key(track, key_ofs, true);
ERR_FAIL_COND(key == -1);
- p_list->push_back(PropertyInfo(Variant::REAL, "time", PROPERTY_HINT_RANGE, "0," + rtos(animation->get_length()) + ",0.01"));
+ if (use_fps && animation->get_step() > 0) {
+ float max_frame = animation->get_length() / animation->get_step();
+ p_list->push_back(PropertyInfo(Variant::REAL, "frame", PROPERTY_HINT_RANGE, "0," + rtos(max_frame) + ",0.01"));
+ } else {
+ p_list->push_back(PropertyInfo(Variant::REAL, "time", PROPERTY_HINT_RANGE, "0," + rtos(animation->get_length()) + ",0.01"));
+ }
switch (animation->track_get_type(track)) {
@@ -648,6 +668,7 @@ public:
PropertyInfo hint;
NodePath base;
+ bool use_fps;
void notify_change() {
@@ -658,7 +679,13 @@ public:
return root_path;
}
+ void set_use_fps(bool p_enable) {
+ use_fps = p_enable;
+ _change_notify();
+ }
+
AnimationTrackKeyEdit() {
+ use_fps = false;
key_ofs = 0;
track = -1;
setting = false;
@@ -690,6 +717,9 @@ void AnimationTimelineEdit::_anim_length_changed(double p_new_len) {
return;
p_new_len = MAX(0.001, p_new_len);
+ if (use_fps && animation->get_step() > 0) {
+ p_new_len *= animation->get_step();
+ }
editing = true;
undo_redo->create_action(TTR("Change Animation Length"));
@@ -887,20 +917,49 @@ void AnimationTimelineEdit::_notification(int p_what) {
decimals = 0;
}
- for (int i = 0; i < zoomw; i++) {
+ if (use_fps) {
+
+ float step_size = animation->get_step();
+ if (step_size > 0) {
+
+ int prev_frame_ofs = -10000000;
- float pos = get_value() + double(i) / scale;
- float prev = get_value() + (double(i) - 1.0) / scale;
+ for (int i = 0; i < zoomw; i++) {
- int sc = int(Math::floor(pos * SC_ADJ));
- int prev_sc = int(Math::floor(prev * SC_ADJ));
- bool sub = (sc % SC_ADJ);
+ float pos = get_value() + double(i) / scale;
+ float prev = get_value() + (double(i) - 1.0) / scale;
- if ((sc / step) != (prev_sc / step) || (prev_sc < 0 && sc >= 0)) {
+ int frame = pos / step_size;
+ int prev_frame = prev / step_size;
- int scd = sc < 0 ? prev_sc : sc;
- draw_line(Point2(get_name_limit() + i, 0), Point2(get_name_limit() + i, h), linecolor);
- draw_string(font, Point2(get_name_limit() + i + 3, (h - font->get_height()) / 2 + font->get_ascent()).floor(), String::num((scd - (scd % step)) / double(SC_ADJ), decimals), sub ? color_time_dec : color_time_sec, zoomw - i);
+ bool sub = Math::floor(prev) == Math::floor(pos);
+
+ if (frame != prev_frame && i >= prev_frame_ofs) {
+
+ draw_line(Point2(get_name_limit() + i, 0), Point2(get_name_limit() + i, h), linecolor);
+
+ draw_string(font, Point2(get_name_limit() + i + 3 * EDSCALE, (h - font->get_height()) / 2 + font->get_ascent()).floor(), itos(frame), sub ? color_time_dec : color_time_sec, zoomw - i);
+ prev_frame_ofs = i + font->get_string_size(itos(frame)).x + 5 * EDSCALE;
+ }
+ }
+ }
+
+ } else {
+ for (int i = 0; i < zoomw; i++) {
+
+ float pos = get_value() + double(i) / scale;
+ float prev = get_value() + (double(i) - 1.0) / scale;
+
+ int sc = int(Math::floor(pos * SC_ADJ));
+ int prev_sc = int(Math::floor(prev * SC_ADJ));
+ bool sub = (sc % SC_ADJ);
+
+ if ((sc / step) != (prev_sc / step) || (prev_sc < 0 && sc >= 0)) {
+
+ int scd = sc < 0 ? prev_sc : sc;
+ draw_line(Point2(get_name_limit() + i, 0), Point2(get_name_limit() + i, h), linecolor);
+ draw_string(font, Point2(get_name_limit() + i + 3, (h - font->get_height()) / 2 + font->get_ascent()).floor(), String::num((scd - (scd % step)) / double(SC_ADJ), decimals), sub ? color_time_dec : color_time_sec, zoomw - i);
+ }
}
}
@@ -961,7 +1020,11 @@ void AnimationTimelineEdit::update_values() {
return;
editing = true;
- length->set_value(animation->get_length());
+ if (use_fps && animation->get_step() > 0) {
+ length->set_value(animation->get_length() / animation->get_step());
+ } else {
+ length->set_value(animation->get_length());
+ }
loop->set_pressed(animation->has_loop());
editing = false;
}
@@ -1046,6 +1109,15 @@ void AnimationTimelineEdit::_gui_input(const Ref<InputEvent> &p_event) {
}
}
+void AnimationTimelineEdit::set_use_fps(bool p_use_fps) {
+ use_fps = p_use_fps;
+ update_values();
+ update();
+}
+bool AnimationTimelineEdit::is_using_fps() const {
+ return use_fps;
+}
+
void AnimationTimelineEdit::set_hscroll(HScrollBar *p_hscroll) {
hscroll = p_hscroll;
@@ -1072,6 +1144,7 @@ void AnimationTimelineEdit::_bind_methods() {
AnimationTimelineEdit::AnimationTimelineEdit() {
+ use_fps = false;
editing = false;
name_limit = 150;
zoom = NULL;
@@ -1099,7 +1172,7 @@ AnimationTimelineEdit::AnimationTimelineEdit() {
len_hb->add_child(time_icon);
length = memnew(EditorSpinSlider);
length->set_min(0.001);
- length->set_max(3600);
+ length->set_max(36000);
length->set_step(0.01);
length->set_allow_greater(true);
length->set_custom_minimum_size(Vector2(70 * EDSCALE, 0));
@@ -2462,10 +2535,12 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim) {
hscroll->show();
edit->set_disabled(false);
step->set_block_signals(true);
- step->set_value(animation->get_step());
+
+ _update_step_spinbox();
step->set_block_signals(false);
step->set_read_only(false);
snap->set_disabled(false);
+ snap_mode->set_disabled(true);
} else {
hscroll->hide();
edit->set_disabled(true);
@@ -2474,6 +2549,7 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim) {
step->set_block_signals(false);
step->set_read_only(true);
snap->set_disabled(true);
+ snap_mode->set_disabled(false);
}
}
@@ -2518,6 +2594,43 @@ void AnimationTrackEditor::update_keying() {
bool AnimationTrackEditor::has_keying() const {
return keying;
}
+Dictionary AnimationTrackEditor::get_state() const {
+ Dictionary state;
+ state["fps_mode"] = timeline->is_using_fps();
+ state["zoom"] = zoom->get_value();
+ state["offset"] = timeline->get_value();
+ state["v_scroll"] = scroll->get_v_scrollbar()->get_value();
+ return state;
+}
+void AnimationTrackEditor::set_state(const Dictionary &p_state) {
+ if (p_state.has("fps_mode")) {
+ bool fps_mode = p_state["fps_mode"];
+ if (fps_mode) {
+ snap_mode->select(1);
+ } else {
+ snap_mode->select(0);
+ }
+ _snap_mode_changed(snap_mode->get_selected());
+ } else {
+ snap_mode->select(0);
+ _snap_mode_changed(snap_mode->get_selected());
+ }
+ if (p_state.has("zoom")) {
+ zoom->set_value(p_state["zoom"]);
+ } else {
+ zoom->set_value(1.0);
+ }
+ if (p_state.has("offset")) {
+ timeline->set_value(p_state["offset"]);
+ } else {
+ timeline->set_value(0);
+ }
+ if (p_state.has("v_scroll")) {
+ scroll->get_v_scrollbar()->set_value(p_state["v_scroll"]);
+ } else {
+ scroll->get_v_scrollbar()->set_value(0);
+ }
+}
void AnimationTrackEditor::cleanup() {
set_animation(Ref<Animation>());
@@ -3417,6 +3530,34 @@ void AnimationTrackEditor::_animation_changed() {
call_deferred("_animation_update");
}
+void AnimationTrackEditor::_snap_mode_changed(int p_mode) {
+
+ timeline->set_use_fps(p_mode == 1);
+ if (key_edit) {
+ key_edit->set_use_fps(p_mode == 1);
+ }
+ _update_step_spinbox();
+}
+
+void AnimationTrackEditor::_update_step_spinbox() {
+ if (!animation.is_valid()) {
+ return;
+ }
+ step->set_block_signals(true);
+
+ if (timeline->is_using_fps()) {
+ if (animation->get_step() == 0) {
+ step->set_value(0);
+ } else {
+ step->set_value(1.0 / animation->get_step());
+ }
+
+ } else {
+ step->set_value(animation->get_step());
+ }
+
+ step->set_block_signals(false);
+}
void AnimationTrackEditor::_animation_update() {
timeline->update();
@@ -3454,9 +3595,7 @@ void AnimationTrackEditor::_animation_update() {
bezier_edit->update();
- step->set_block_signals(true);
- step->set_value(animation->get_step());
- step->set_block_signals(false);
+ _update_step_spinbox();
animation_changing_awaiting_update = false;
}
@@ -3497,12 +3636,18 @@ void AnimationTrackEditor::_update_scroll(double) {
void AnimationTrackEditor::_update_step(double p_new_step) {
undo_redo->create_action(TTR("Change Animation Step"));
- undo_redo->add_do_method(animation.ptr(), "set_step", p_new_step);
+ float step_value = p_new_step;
+ if (timeline->is_using_fps()) {
+ if (step_value != 0.0) {
+ step_value = 1.0 / step_value;
+ }
+ }
+ undo_redo->add_do_method(animation.ptr(), "set_step", step_value);
undo_redo->add_undo_method(animation.ptr(), "set_step", animation->get_step());
step->set_block_signals(true);
undo_redo->commit_action();
step->set_block_signals(false);
- emit_signal("animation_step_changed", p_new_step);
+ emit_signal("animation_step_changed", step_value);
}
void AnimationTrackEditor::_update_length(double p_new_len) {
@@ -4787,6 +4932,7 @@ void AnimationTrackEditor::_bind_methods() {
ClassDB::bind_method("_edit_menu_pressed", &AnimationTrackEditor::_edit_menu_pressed);
ClassDB::bind_method("_view_group_toggle", &AnimationTrackEditor::_view_group_toggle);
ClassDB::bind_method("_selection_changed", &AnimationTrackEditor::_selection_changed);
+ ClassDB::bind_method("_snap_mode_changed", &AnimationTrackEditor::_snap_mode_changed);
ADD_SIGNAL(MethodInfo("timeline_changed", PropertyInfo(Variant::REAL, "position"), PropertyInfo(Variant::BOOL, "drag")));
ADD_SIGNAL(MethodInfo("keying_changed"));
@@ -4875,7 +5021,7 @@ AnimationTrackEditor::AnimationTrackEditor() {
bottom_hb->add_child(memnew(VSeparator));
snap = memnew(ToolButton);
- snap->set_text(TTR("Snap (s): "));
+ snap->set_text(TTR("Snap: "));
bottom_hb->add_child(snap);
snap->set_disabled(true);
snap->set_toggle_mode(true);
@@ -4883,7 +5029,7 @@ AnimationTrackEditor::AnimationTrackEditor() {
step = memnew(EditorSpinSlider);
step->set_min(0);
- step->set_max(1000);
+ step->set_max(1000000);
step->set_step(0.01);
step->set_hide_slider(true);
step->set_custom_minimum_size(Size2(100, 0) * EDSCALE);
@@ -4892,6 +5038,13 @@ AnimationTrackEditor::AnimationTrackEditor() {
step->connect("value_changed", this, "_update_step");
step->set_read_only(true);
+ snap_mode = memnew(OptionButton);
+ snap_mode->add_item(TTR("Seconds"));
+ snap_mode->add_item(TTR("FPS"));
+ bottom_hb->add_child(snap_mode);
+ snap_mode->connect("item_selected", this, "_snap_mode_changed");
+ snap_mode->set_disabled(true);
+
bottom_hb->add_child(memnew(VSeparator));
zoom_icon = memnew(TextureRect);
diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h
index 29ce4f189e..5ac5999b68 100644
--- a/editor/animation_track_editor.h
+++ b/editor/animation_track_editor.h
@@ -76,6 +76,7 @@ class AnimationTimelineEdit : public Range {
Rect2 hsize_rect;
bool editing;
+ bool use_fps;
bool panning_timeline;
float panning_timeline_from;
@@ -110,6 +111,9 @@ public:
void update_values();
+ void set_use_fps(bool p_use_fps);
+ bool is_using_fps() const;
+
void set_hscroll(HScrollBar *p_hscroll);
AnimationTimelineEdit();
@@ -303,7 +307,9 @@ class AnimationTrackEditor : public VBoxContainer {
EditorSpinSlider *step;
TextureRect *zoom_icon;
ToolButton *snap;
+ OptionButton *snap_mode;
+ void _snap_mode_changed(int p_mode);
Vector<AnimationTrackEdit *> track_edits;
Vector<AnimationTrackEditGroup *> groups;
@@ -328,6 +334,8 @@ class AnimationTrackEditor : public VBoxContainer {
void _new_track_node_selected(NodePath p_path);
void _new_track_property_selected(String p_name);
+ void _update_step_spinbox();
+
PropertySelector *prop_selector;
PropertySelector *method_selector;
SceneTreeDialog *pick_track;
@@ -484,6 +492,9 @@ public:
void update_keying();
bool has_keying() const;
+ Dictionary get_state() const;
+ void set_state(const Dictionary &p_state);
+
void cleanup();
void set_anim_pos(float p_pos);
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 0869f6ce77..9641e10114 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -687,7 +687,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_constant("button_margin", "Tree", default_margin_size * EDSCALE);
theme->set_constant("draw_relationship_lines", "Tree", relationship_line_opacity >= 0.01);
theme->set_constant("draw_guides", "Tree", relationship_line_opacity < 0.01);
- theme->set_constant("scroll_border", "Tree", default_margin_size * EDSCALE);
+ theme->set_constant("scroll_border", "Tree", 40 * EDSCALE);
theme->set_constant("scroll_speed", "Tree", 12);
Ref<StyleBoxFlat> style_tree_btn = style_default->duplicate();
diff --git a/editor/icons/icon_auto_key.svg b/editor/icons/icon_auto_key.svg
new file mode 100644
index 0000000000..cbafe1ac38
--- /dev/null
+++ b/editor/icons/icon_auto_key.svg
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg6"
+ sodipodi:docname="icon_auto_key.svg"
+ inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
+ <metadata
+ id="metadata12">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs10" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1854"
+ inkscape:window-height="1016"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="10.429825"
+ inkscape:cx="10.199345"
+ inkscape:cy="-4.0344119"
+ inkscape:window-x="66"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg6" />
+ <path
+ style="fill:#e0e0e0;fill-opacity:1;stroke-width:0.0333107"
+ d="M 3.5469681,13.426786 C 2.7965829,13.263778 2.2774312,12.503915 2.4037297,11.753472 c 0.1081234,-0.642451 0.6006808,-1.135008 1.2431317,-1.243131 0.9667125,-0.162696 1.8555225,0.726112 1.6928259,1.692826 -0.103766,0.616558 -0.5592173,1.098057 -1.1588427,1.225117 -0.2719576,0.05763 -0.3626872,0.05741 -0.6338765,-0.0014 z m 8.0861339,-0.08275 c -0.746862,-0.13829 -1.23937,-0.720718 -1.23937,-1.465649 0,-0.527377 0.244831,-0.978806 0.679757,-1.253362 0.471386,-0.297574 1.114188,-0.297574 1.585574,0 0.682727,0.430986 0.892336,1.362194 0.460575,2.046149 -0.307786,0.487563 -0.940521,0.773963 -1.486536,0.672862 z M 0.60726032,9.8305658 V 7.7161233 L 1.1770842,7.7070075 1.7469079,7.6978939 3.1889882,5.1995916 4.6310686,2.7012893 h 3.1726318 3.1726316 l 1.442755,2.4983023 1.442755,2.4983023 0.651097,0.00903 0.651096,0.00903 v 2.1145264 2.1145257 h -0.566282 -0.566281 v -0.161225 c 0,-0.234927 -0.113135,-0.639704 -0.255664,-0.914727 -0.16895,-0.326004 -0.574198,-0.731251 -0.900202,-0.9002019 -0.656732,-0.3403483 -1.428549,-0.3403483 -2.085281,0 -0.326004,0.1689519 -0.731252,0.5741989 -0.9002019,0.9002029 -0.1425297,0.275023 -0.2556639,0.6798 -0.2556639,0.914727 v 0.161225 H 7.8570969 6.0797346 L 6.0617736,11.686851 C 6.006289,10.889347 5.447548,10.170679 4.6603773,9.884336 4.4466221,9.8065798 4.3737631,9.797427 3.9716406,9.7978134 3.5871254,9.7981885 3.4905638,9.809405 3.3054265,9.8752358 2.5067319,10.159236 1.9362359,10.884501 1.8813215,11.68568 l -0.017772,0.259329 H 1.2354063 0.60726287 Z M 12.399247,7.7466889 c 0,-0.037287 -0.02623,-0.1073444 -0.0583,-0.1556843 -0.03206,-0.04834 -0.561225,-0.958444 -1.17592,-2.0224529 L 10.047407,3.6339894 7.6977565,3.6254406 C 5.4917229,3.6174174 5.3450379,3.6204563 5.2979001,3.6754094 5.1898818,3.8013046 2.9723198,7.6840061 2.9723198,7.7472381 c 0,0.067139 0.00758,0.067247 4.7134636,0.067247 h 4.7134636 z"
+ id="path6243"
+ inkscape:connector-curvature="0" />
+</svg>
diff --git a/editor/icons/icon_sprite_sheet.svg b/editor/icons/icon_sprite_sheet.svg
new file mode 100644
index 0000000000..eeb804f8b9
--- /dev/null
+++ b/editor/icons/icon_sprite_sheet.svg
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg6"
+ sodipodi:docname="icon_sprite_sheet.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata12">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs10" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="773"
+ inkscape:window-height="480"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="14.75"
+ inkscape:cx="8"
+ inkscape:cy="8"
+ inkscape:window-x="551"
+ inkscape:window-y="278"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="g4" />
+ <g
+ transform="translate(0 -1036.4)"
+ id="g4">
+ <path
+ transform="translate(0 1036.4)"
+ d="m3 1c-1.1046 0-2 0.89543-2 2v10c0 1.1046 0.89543 2 2 2h10c1.1046 0 2-0.89543 2-2v-10c0-1.1046-0.89543-2-2-2h-10zm0 2h2v2h-2v-2zm4 0h2v2h-2v-2zm4 0h2v2h-2v-2zm-8 4h2v2h-2v-2zm4 0h2v2h-2v-2zm4 0h2v2h-2v-2zm-8 4h2v2h-2v-2zm4 0h2v2h-2v-2zm4 0h2v2h-2v-2z"
+ fill="#a5efac"
+ id="path2"
+ style="fill:#e0e0e0;fill-opacity:1" />
+ </g>
+</svg>
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index bbaf41e3cc..41f35c3bed 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -670,6 +670,7 @@ Dictionary AnimationPlayerEditor::get_state() const {
if (EditorNode::get_singleton()->get_edited_scene() && is_visible_in_tree() && player) {
d["player"] = EditorNode::get_singleton()->get_edited_scene()->get_path_to(player);
d["animation"] = player->get_assigned_animation();
+ d["track_editor_state"] = track_editor->get_state();
}
return d;
@@ -696,6 +697,10 @@ void AnimationPlayerEditor::set_state(const Dictionary &p_state) {
_animation_edit();
}
}
+
+ if (p_state.has("track_editor_state")) {
+ track_editor->set_state(p_state["track_editor_state"]);
+ }
}
}
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index c3cac582ad..b2923a1ff2 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -1340,6 +1340,10 @@ bool CanvasItemEditor::_gui_input_rotate(const Ref<InputEvent> &p_event) {
// Confirms the node rotation
if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && !b->is_pressed()) {
_commit_canvas_item_state(drag_selection, TTR("Rotate CanvasItem"));
+ if (key_auto_insert_button->is_pressed()) {
+ _insert_animation_keys(false, true, false, true);
+ }
+
drag_type = DRAG_NONE;
return true;
}
@@ -1641,6 +1645,9 @@ bool CanvasItemEditor::_gui_input_resize(const Ref<InputEvent> &p_event) {
// Confirm resize
if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && !b->is_pressed()) {
_commit_canvas_item_state(drag_selection, TTR("Resize CanvasItem"));
+ if (key_auto_insert_button->is_pressed()) {
+ _insert_animation_keys(false, false, true, true);
+ }
drag_type = DRAG_NONE;
viewport->update();
return true;
@@ -1747,6 +1754,10 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) {
// Confirm resize
if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && !b->is_pressed()) {
_commit_canvas_item_state(drag_selection, TTR("Scale CanvasItem"));
+ if (key_auto_insert_button->is_pressed()) {
+ _insert_animation_keys(false, false, true, true);
+ }
+
drag_type = DRAG_NONE;
viewport->update();
return true;
@@ -1852,6 +1863,9 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) {
_commit_canvas_item_state(drag_selection, TTR("Move CanvasItem"), true);
}
+ if (key_auto_insert_button->is_pressed()) {
+ _insert_animation_keys(true, false, false, true);
+ }
drag_type = DRAG_NONE;
viewport->update();
return true;
@@ -3384,6 +3398,7 @@ void CanvasItemEditor::_notification(int p_what) {
key_rot_button->set_icon(get_icon("KeyRotation", "EditorIcons"));
key_scale_button->set_icon(get_icon("KeyScale", "EditorIcons"));
key_insert_button->set_icon(get_icon("Key", "EditorIcons"));
+ key_auto_insert_button->set_icon(get_icon("AutoKey", "EditorIcons"));
zoom_minus->set_icon(get_icon("ZoomLess", "EditorIcons"));
zoom_reset->set_icon(get_icon("ZoomReset", "EditorIcons"));
@@ -3716,6 +3731,77 @@ void CanvasItemEditor::_button_tool_select(int p_index) {
tool = (Tool)p_index;
}
+void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation, bool p_scale, bool p_on_existing) {
+
+ Map<Node *, Object *> &selection = editor_selection->get_selection();
+
+ for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) {
+
+ CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->key());
+ if (!canvas_item || !canvas_item->is_visible_in_tree())
+ continue;
+
+ if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root())
+ continue;
+
+ if (Object::cast_to<Node2D>(canvas_item)) {
+ Node2D *n2d = Object::cast_to<Node2D>(canvas_item);
+
+ if (key_pos && p_location)
+ AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "position", n2d->get_position(), p_on_existing);
+ if (key_rot && p_rotation)
+ AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "rotation_degrees", Math::rad2deg(n2d->get_rotation()), p_on_existing);
+ if (key_scale && p_scale)
+ AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "scale", n2d->get_scale(), p_on_existing);
+
+ if (n2d->has_meta("_edit_bone_") && n2d->get_parent_item()) {
+ //look for an IK chain
+ List<Node2D *> ik_chain;
+
+ Node2D *n = Object::cast_to<Node2D>(n2d->get_parent_item());
+ bool has_chain = false;
+
+ while (n) {
+
+ ik_chain.push_back(n);
+ if (n->has_meta("_edit_ik_")) {
+ has_chain = true;
+ break;
+ }
+
+ if (!n->get_parent_item())
+ break;
+ n = Object::cast_to<Node2D>(n->get_parent_item());
+ }
+
+ if (has_chain && ik_chain.size()) {
+
+ for (List<Node2D *>::Element *F = ik_chain.front(); F; F = F->next()) {
+
+ if (key_pos)
+ AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F->get(), "position", F->get()->get_position(), p_on_existing);
+ if (key_rot)
+ AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F->get(), "rotation_degrees", Math::rad2deg(F->get()->get_rotation()), p_on_existing);
+ if (key_scale)
+ AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F->get(), "scale", F->get()->get_scale(), p_on_existing);
+ }
+ }
+ }
+
+ } else if (Object::cast_to<Control>(canvas_item)) {
+
+ Control *ctrl = Object::cast_to<Control>(canvas_item);
+
+ if (key_pos)
+ AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_position", ctrl->get_position(), p_on_existing);
+ if (key_rot)
+ AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_rotation", ctrl->get_rotation_degrees(), p_on_existing);
+ if (key_scale)
+ AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_size", ctrl->get_size(), p_on_existing);
+ }
+ }
+}
+
void CanvasItemEditor::_popup_callback(int p_op) {
last_option = MenuOption(p_op);
@@ -3983,73 +4069,7 @@ void CanvasItemEditor::_popup_callback(int p_op) {
bool existing = p_op == ANIM_INSERT_KEY_EXISTING;
- Map<Node *, Object *> &selection = editor_selection->get_selection();
-
- for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) {
-
- CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->key());
- if (!canvas_item || !canvas_item->is_visible_in_tree())
- continue;
-
- if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root())
- continue;
-
- if (Object::cast_to<Node2D>(canvas_item)) {
- Node2D *n2d = Object::cast_to<Node2D>(canvas_item);
-
- if (key_pos)
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "position", n2d->get_position(), existing);
- if (key_rot)
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "rotation_degrees", Math::rad2deg(n2d->get_rotation()), existing);
- if (key_scale)
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "scale", n2d->get_scale(), existing);
-
- if (n2d->has_meta("_edit_bone_") && n2d->get_parent_item()) {
- //look for an IK chain
- List<Node2D *> ik_chain;
-
- Node2D *n = Object::cast_to<Node2D>(n2d->get_parent_item());
- bool has_chain = false;
-
- while (n) {
-
- ik_chain.push_back(n);
- if (n->has_meta("_edit_ik_")) {
- has_chain = true;
- break;
- }
-
- if (!n->get_parent_item())
- break;
- n = Object::cast_to<Node2D>(n->get_parent_item());
- }
-
- if (has_chain && ik_chain.size()) {
-
- for (List<Node2D *>::Element *F = ik_chain.front(); F; F = F->next()) {
-
- if (key_pos)
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F->get(), "position", F->get()->get_position(), existing);
- if (key_rot)
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F->get(), "rotation_degrees", Math::rad2deg(F->get()->get_rotation()), existing);
- if (key_scale)
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F->get(), "scale", F->get()->get_scale(), existing);
- }
- }
- }
-
- } else if (Object::cast_to<Control>(canvas_item)) {
-
- Control *ctrl = Object::cast_to<Control>(canvas_item);
-
- if (key_pos)
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_position", ctrl->get_position(), existing);
- if (key_rot)
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_rotation", ctrl->get_rotation_degrees(), existing);
- if (key_scale)
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_size", ctrl->get_size(), existing);
- }
- }
+ _insert_animation_keys(true, true, true, existing);
} break;
case ANIM_INSERT_POS: {
@@ -4866,6 +4886,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
key_loc_button->set_pressed(true);
key_loc_button->set_focus_mode(FOCUS_NONE);
key_loc_button->connect("pressed", this, "_popup_callback", varray(ANIM_INSERT_POS));
+ key_loc_button->set_tooltip(TTR("Translation mask for inserting keys."));
animation_hb->add_child(key_loc_button);
key_rot_button = memnew(Button);
key_rot_button->set_toggle_mode(true);
@@ -4873,21 +4894,30 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
key_rot_button->set_pressed(true);
key_rot_button->set_focus_mode(FOCUS_NONE);
key_rot_button->connect("pressed", this, "_popup_callback", varray(ANIM_INSERT_ROT));
+ key_rot_button->set_tooltip(TTR("Rotation mask for inserting keys."));
animation_hb->add_child(key_rot_button);
key_scale_button = memnew(Button);
key_scale_button->set_toggle_mode(true);
key_scale_button->set_flat(true);
key_scale_button->set_focus_mode(FOCUS_NONE);
key_scale_button->connect("pressed", this, "_popup_callback", varray(ANIM_INSERT_SCALE));
+ key_scale_button->set_tooltip(TTR("Scale mask for inserting keys."));
animation_hb->add_child(key_scale_button);
key_insert_button = memnew(Button);
key_insert_button->set_flat(true);
key_insert_button->set_focus_mode(FOCUS_NONE);
key_insert_button->connect("pressed", this, "_popup_callback", varray(ANIM_INSERT_KEY));
- key_insert_button->set_tooltip(TTR("Insert keys."));
+ key_insert_button->set_tooltip(TTR("Insert keys (based on mask)."));
key_insert_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/anim_insert_key", TTR("Insert Key"), KEY_INSERT));
-
animation_hb->add_child(key_insert_button);
+ key_auto_insert_button = memnew(Button);
+ key_auto_insert_button->set_flat(true);
+ key_auto_insert_button->set_toggle_mode(true);
+ key_auto_insert_button->set_focus_mode(FOCUS_NONE);
+ //key_auto_insert_button->connect("pressed", this, "_popup_callback", varray(ANIM_INSERT_KEY));
+ key_auto_insert_button->set_tooltip(TTR("Auto insert keys when objects are translated, rotated on scaled (based on mask).\nKeys are only added to existing tracks, no new tracks will be created.\nKeys must be inserted manually for the first time."));
+ key_auto_insert_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/anim_auto_insert_key", TTR("Auto Insert Key")));
+ animation_hb->add_child(key_auto_insert_button);
animation_menu = memnew(MenuButton);
animation_menu->set_text(TTR("Animation"));
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index 9173c55ae0..14ea81f302 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -351,6 +351,7 @@ private:
Button *key_rot_button;
Button *key_scale_button;
Button *key_insert_button;
+ Button *key_auto_insert_button;
PopupMenu *selection_menu;
@@ -422,6 +423,8 @@ private:
Object *_get_editor_data(Object *p_what);
+ void _insert_animation_keys(bool p_location, bool p_rotation, bool p_scale, bool p_on_existing);
+
void _keying_changed();
void _unhandled_key_input(const Ref<InputEvent> &p_ev);
diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp
index 5ba2fde763..33b8347f94 100644
--- a/editor/plugins/sprite_frames_editor_plugin.cpp
+++ b/editor/plugins/sprite_frames_editor_plugin.cpp
@@ -38,11 +38,167 @@
void SpriteFramesEditor::_gui_input(Ref<InputEvent> p_event) {
}
+void SpriteFramesEditor::_open_sprite_sheet() {
+
+ file_split_sheet->clear_filters();
+ List<String> extensions;
+ ResourceLoader::get_recognized_extensions_for_type("Texture", &extensions);
+ for (int i = 0; i < extensions.size(); i++) {
+ file_split_sheet->add_filter("*." + extensions[i]);
+ }
+
+ file_split_sheet->popup_centered_ratio();
+}
+
+void SpriteFramesEditor::_sheet_preview_draw() {
+ Size2i size = split_sheet_preview->get_size();
+ int h = split_sheet_h->get_value();
+ int v = split_sheet_v->get_value();
+ const float a = 0.3;
+ for (int i = 1; i < h; i++) {
+ for (int j = 1; j < v; j++) {
+
+ int x = i * size.width / h;
+ int y = i * size.height / v;
+
+ split_sheet_preview->draw_line(Point2(x, 0), Point2(x, size.height), Color(1, 1, 1, a));
+ split_sheet_preview->draw_line(Point2(x + 1, 0), Point2(x + 1, size.height), Color(0, 0, 0, a));
+
+ split_sheet_preview->draw_line(Point2(0, y), Point2(size.width, y), Color(1, 1, 1, a));
+ split_sheet_preview->draw_line(Point2(0, y + 1), Point2(size.width, y + 1), Color(0, 0, 0, a));
+ }
+ }
+
+ Color accent = get_color("accent_color", "Editor");
+
+ for (Set<int>::Element *E = frames_selected.front(); E; E = E->next()) {
+ int idx = E->get();
+ int x = (idx % h) * size.width / h;
+ int y = (idx / v) * size.height / v;
+ int width = size.width / h;
+ int height = size.height / v;
+
+ split_sheet_preview->draw_rect(Rect2(x + 5, y + 5, width - 10, height - 10), Color(0, 0, 0, 0.35), true);
+ split_sheet_preview->draw_rect(Rect2(x + 0, y + 0, width - 0, height - 0), Color(0, 0, 0, 1), false);
+ split_sheet_preview->draw_rect(Rect2(x + 1, y + 1, width - 2, height - 2), Color(0, 0, 0, 1), false);
+ split_sheet_preview->draw_rect(Rect2(x + 2, y + 2, width - 4, height - 4), accent, false);
+ split_sheet_preview->draw_rect(Rect2(x + 3, y + 3, width - 6, height - 6), accent, false);
+ split_sheet_preview->draw_rect(Rect2(x + 4, y + 4, width - 8, height - 8), Color(0, 0, 0, 1), false);
+ split_sheet_preview->draw_rect(Rect2(x + 5, y + 5, width - 10, height - 10), Color(0, 0, 0, 1), false);
+ }
+
+ if (frames_selected.size() == 0) {
+ split_sheet_dialog->get_ok()->set_disabled(true);
+ split_sheet_dialog->get_ok()->set_text(TTR("No frames selected"));
+ } else {
+ split_sheet_dialog->get_ok()->set_disabled(false);
+ split_sheet_dialog->get_ok()->set_text(vformat(TTR("Add %d frame(s)"), frames_selected.size()));
+ }
+}
+void SpriteFramesEditor::_sheet_preview_input(const Ref<InputEvent> &p_event) {
+
+ Ref<InputEventMouseButton> mb = p_event;
+
+ if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+ Size2i size = split_sheet_preview->get_size();
+ int h = split_sheet_h->get_value();
+ int v = split_sheet_v->get_value();
+
+ int x = CLAMP(int(mb->get_position().x) * h / size.width, 0, h - 1);
+ int y = CLAMP(int(mb->get_position().y) * v / size.height, 0, v - 1);
+
+ int idx = h * y + x;
+
+ if (mb->get_shift() && last_frame_selected >= 0) {
+ //select multiple
+ int from = idx;
+ int to = last_frame_selected;
+ if (from > to) {
+ SWAP(from, to);
+ }
+
+ for (int i = from; i <= to; i++) {
+ if (mb->get_control()) {
+ frames_selected.erase(i);
+ } else {
+ frames_selected.insert(i);
+ }
+ }
+ } else {
+ if (frames_selected.has(idx)) {
+ frames_selected.erase(idx);
+ } else {
+ frames_selected.insert(idx);
+ }
+ }
+
+ last_frame_selected = idx;
+ split_sheet_preview->update();
+ }
+}
+
+void SpriteFramesEditor::_sheet_add_frames() {
+
+ Size2i size = split_sheet_preview->get_size();
+ int h = split_sheet_h->get_value();
+ int v = split_sheet_v->get_value();
+
+ undo_redo->create_action(TTR("Add Frame"));
+
+ int fc = frames->get_frame_count(edited_anim);
+
+ for (Set<int>::Element *E = frames_selected.front(); E; E = E->next()) {
+ int idx = E->get();
+ int x = (idx % h) * size.width / h;
+ int y = (idx / v) * size.height / v;
+ int width = size.width / h;
+ int height = size.height / v;
+
+ Ref<AtlasTexture> at;
+ at.instance();
+ at->set_atlas(split_sheet_preview->get_texture());
+ at->set_region(Rect2(x, y, width, height));
+
+ undo_redo->add_do_method(frames, "add_frame", edited_anim, at, -1);
+ undo_redo->add_undo_method(frames, "remove_frame", edited_anim, fc);
+ }
+
+ undo_redo->add_do_method(this, "_update_library");
+ undo_redo->add_undo_method(this, "_update_library");
+ undo_redo->commit_action();
+}
+
+void SpriteFramesEditor::_sheet_spin_changed(double) {
+ frames_selected.clear();
+ last_frame_selected = -1;
+ split_sheet_preview->update();
+}
+
+void SpriteFramesEditor::_prepare_sprite_sheet(const String &p_file) {
+
+ Ref<Resource> texture = ResourceLoader::load(p_file);
+ if (!texture.is_valid()) {
+ EditorNode::get_singleton()->show_warning("Unable to load images");
+ ERR_FAIL_COND(!texture.is_valid());
+ }
+ if (texture != split_sheet_preview->get_texture()) {
+ //different texture, reset to 4x4
+ split_sheet_h->set_value(4);
+ split_sheet_v->set_value(4);
+ }
+ frames_selected.clear();
+ last_frame_selected = -1;
+
+ split_sheet_preview->set_texture(texture);
+ split_sheet_dialog->popup_centered_ratio(0.65);
+}
+
void SpriteFramesEditor::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
load->set_icon(get_icon("Load", "EditorIcons"));
+ load_sheet->set_icon(get_icon("SpriteSheet", "EditorIcons"));
copy->set_icon(get_icon("ActionCopy", "EditorIcons"));
paste->set_icon(get_icon("ActionPaste", "EditorIcons"));
empty->set_icon(get_icon("InsertBefore", "EditorIcons"));
@@ -72,6 +228,7 @@ void SpriteFramesEditor::_file_load_request(const PoolVector<String> &p_path, in
if (resource.is_null()) {
dialog->set_text(TTR("ERROR: Couldn't load frame resource!"));
dialog->set_title(TTR("Error!"));
+
//dialog->get_cancel()->set_text("Close");
dialog->get_ok()->set_text(TTR("Close"));
dialog->popup_centered_minsize();
@@ -655,6 +812,12 @@ void SpriteFramesEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &SpriteFramesEditor::get_drag_data_fw);
ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &SpriteFramesEditor::can_drop_data_fw);
ClassDB::bind_method(D_METHOD("drop_data_fw"), &SpriteFramesEditor::drop_data_fw);
+ ClassDB::bind_method(D_METHOD("_prepare_sprite_sheet"), &SpriteFramesEditor::_prepare_sprite_sheet);
+ ClassDB::bind_method(D_METHOD("_open_sprite_sheet"), &SpriteFramesEditor::_open_sprite_sheet);
+ ClassDB::bind_method(D_METHOD("_sheet_preview_draw"), &SpriteFramesEditor::_sheet_preview_draw);
+ ClassDB::bind_method(D_METHOD("_sheet_preview_input"), &SpriteFramesEditor::_sheet_preview_input);
+ ClassDB::bind_method(D_METHOD("_sheet_spin_changed"), &SpriteFramesEditor::_sheet_spin_changed);
+ ClassDB::bind_method(D_METHOD("_sheet_add_frames"), &SpriteFramesEditor::_sheet_add_frames);
}
SpriteFramesEditor::SpriteFramesEditor() {
@@ -712,9 +875,15 @@ SpriteFramesEditor::SpriteFramesEditor() {
sub_vb->add_child(hbc);
load = memnew(ToolButton);
- load->set_tooltip(TTR("Load Resource"));
+ load->set_tooltip(TTR("Add a Texture from File"));
hbc->add_child(load);
+ load_sheet = memnew(ToolButton);
+ load_sheet->set_tooltip(TTR("Add frames from a Sprite Sheet"));
+ hbc->add_child(load_sheet);
+
+ hbc->add_child(memnew(VSeparator));
+
copy = memnew(ToolButton);
copy->set_tooltip(TTR("Copy"));
hbc->add_child(copy);
@@ -723,6 +892,8 @@ SpriteFramesEditor::SpriteFramesEditor() {
paste->set_tooltip(TTR("Paste"));
hbc->add_child(paste);
+ hbc->add_spacer(false);
+
empty = memnew(ToolButton);
empty->set_tooltip(TTR("Insert Empty (Before)"));
hbc->add_child(empty);
@@ -731,7 +902,7 @@ SpriteFramesEditor::SpriteFramesEditor() {
empty2->set_tooltip(TTR("Insert Empty (After)"));
hbc->add_child(empty2);
- hbc->add_spacer(false);
+ hbc->add_child(memnew(VSeparator));
move_up = memnew(ToolButton);
move_up->set_tooltip(TTR("Move (Before)"));
@@ -766,6 +937,7 @@ SpriteFramesEditor::SpriteFramesEditor() {
add_child(dialog);
load->connect("pressed", this, "_load_pressed");
+ load_sheet->connect("pressed", this, "_open_sprite_sheet");
_delete->connect("pressed", this, "_delete_pressed");
copy->connect("pressed", this, "_copy_pressed");
paste->connect("pressed", this, "_paste_pressed");
@@ -780,6 +952,60 @@ SpriteFramesEditor::SpriteFramesEditor() {
updating = false;
edited_anim = "default";
+
+ split_sheet_dialog = memnew(ConfirmationDialog);
+ add_child(split_sheet_dialog);
+ VBoxContainer *split_sheet_vb = memnew(VBoxContainer);
+ split_sheet_dialog->add_child(split_sheet_vb);
+ split_sheet_dialog->set_title(TTR("Select Frames"));
+ split_sheet_dialog->connect("confirmed", this, "_sheet_add_frames");
+
+ ScrollContainer *scroll = memnew(ScrollContainer);
+ split_sheet_preview = memnew(TextureRect);
+ split_sheet_preview->set_expand(false);
+ split_sheet_preview->set_mouse_filter(MOUSE_FILTER_PASS);
+ split_sheet_preview->connect("draw", this, "_sheet_preview_draw");
+ split_sheet_preview->connect("gui_input", this, "_sheet_preview_input");
+
+ scroll->set_enable_h_scroll(true);
+ scroll->set_enable_v_scroll(true);
+ CenterContainer *cc = memnew(CenterContainer);
+ cc->add_child(split_sheet_preview);
+ cc->set_h_size_flags(SIZE_EXPAND_FILL);
+ cc->set_v_size_flags(SIZE_EXPAND_FILL);
+ scroll->add_child(cc);
+
+ split_sheet_vb->add_margin_child(TTR("Base Image:"), scroll, true);
+
+ HBoxContainer *split_sheet_hb = memnew(HBoxContainer);
+ split_sheet_hb->add_spacer();
+ Label *ss_label = memnew(Label(TTR("Horizontal:")));
+ split_sheet_hb->add_child(ss_label);
+ split_sheet_h = memnew(SpinBox);
+ split_sheet_h->set_min(1);
+ split_sheet_h->set_max(128);
+ split_sheet_h->set_step(1);
+ split_sheet_hb->add_child(split_sheet_h);
+ split_sheet_hb->add_spacer();
+ split_sheet_h->connect("value_changed", this, "_sheet_spin_changed");
+
+ ss_label = memnew(Label(TTR("Vertical:")));
+ split_sheet_hb->add_child(ss_label);
+ split_sheet_v = memnew(SpinBox);
+ split_sheet_v->set_min(1);
+ split_sheet_v->set_max(128);
+ split_sheet_v->set_step(1);
+ split_sheet_hb->add_child(split_sheet_v);
+ split_sheet_hb->add_spacer();
+ split_sheet_v->connect("value_changed", this, "_sheet_spin_changed");
+
+ split_sheet_vb->add_margin_child(TTR("Split Settings:"), split_sheet_hb);
+
+ file_split_sheet = memnew(EditorFileDialog);
+ file_split_sheet->set_title(TTR("Create frames from Sprite Sheet"));
+ file_split_sheet->set_mode(EditorFileDialog::MODE_OPEN_FILE);
+ add_child(file_split_sheet);
+ file_split_sheet->connect("file_selected", this, "_prepare_sprite_sheet");
}
void SpriteFramesEditorPlugin::edit(Object *p_object) {
diff --git a/editor/plugins/sprite_frames_editor_plugin.h b/editor/plugins/sprite_frames_editor_plugin.h
index 55dd10074e..383e99f87e 100644
--- a/editor/plugins/sprite_frames_editor_plugin.h
+++ b/editor/plugins/sprite_frames_editor_plugin.h
@@ -37,6 +37,7 @@
#include "scene/gui/dialogs.h"
#include "scene/gui/file_dialog.h"
#include "scene/gui/split_container.h"
+#include "scene/gui/texture_rect.h"
#include "scene/gui/tree.h"
class SpriteFramesEditor : public HSplitContainer {
@@ -44,6 +45,7 @@ class SpriteFramesEditor : public HSplitContainer {
GDCLASS(SpriteFramesEditor, HSplitContainer);
ToolButton *load;
+ ToolButton *load_sheet;
ToolButton *_delete;
ToolButton *copy;
ToolButton *paste;
@@ -71,6 +73,14 @@ class SpriteFramesEditor : public HSplitContainer {
StringName edited_anim;
+ ConfirmationDialog *split_sheet_dialog;
+ TextureRect *split_sheet_preview;
+ SpinBox *split_sheet_h;
+ SpinBox *split_sheet_v;
+ EditorFileDialog *file_split_sheet;
+ Set<int> frames_selected;
+ int last_frame_selected;
+
void _load_pressed();
void _load_scene_pressed();
void _file_load_request(const PoolVector<String> &p_path, int p_at_pos = -1);
@@ -99,6 +109,13 @@ class SpriteFramesEditor : public HSplitContainer {
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
+ void _open_sprite_sheet();
+ void _prepare_sprite_sheet(const String &p_file);
+ void _sheet_preview_draw();
+ void _sheet_spin_changed(double);
+ void _sheet_preview_input(const Ref<InputEvent> &p_event);
+ void _sheet_add_frames();
+
protected:
void _notification(int p_what);
void _gui_input(Ref<InputEvent> p_event);
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 0eeb104777..0aba7f3d15 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -1004,6 +1004,58 @@ void VisualShaderEditor::_duplicate_nodes() {
}
}
+void VisualShaderEditor::_on_nodes_delete() {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+ List<int> to_erase;
+
+ for (int i = 0; i < graph->get_child_count(); i++) {
+ GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
+ if (gn) {
+ if (gn->is_selected() && gn->is_close_button_visible()) {
+ to_erase.push_back(gn->get_name().operator String().to_int());
+ }
+ }
+ }
+
+ if (to_erase.empty())
+ return;
+
+ undo_redo->create_action(TTR("Delete Nodes"));
+
+ for (List<int>::Element *F = to_erase.front(); F; F = F->next()) {
+ undo_redo->add_do_method(visual_shader.ptr(), "remove_node", type, F->get());
+ undo_redo->add_undo_method(visual_shader.ptr(), "add_node", type, visual_shader->get_node(type, F->get()), visual_shader->get_node_position(type, F->get()), F->get());
+ }
+
+ List<VisualShader::Connection> conns;
+ visual_shader->get_node_connections(type, &conns);
+
+ List<VisualShader::Connection> used_conns;
+ for (List<int>::Element *F = to_erase.front(); F; F = F->next()) {
+ for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) {
+ if (E->get().from_node == F->get() || E->get().to_node == F->get()) {
+
+ bool cancel = false;
+ for (List<VisualShader::Connection>::Element *R = used_conns.front(); R; R = R->next()) {
+ if (R->get().from_node == E->get().from_node && R->get().from_port == E->get().from_port && R->get().to_node == E->get().to_node && R->get().to_port == E->get().to_port) {
+ cancel = true; // to avoid ERR_ALREADY_EXISTS warning
+ break;
+ }
+ }
+ if (!cancel) {
+ undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes", type, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port);
+ used_conns.push_back(E->get());
+ }
+ }
+ }
+ }
+
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+}
+
void VisualShaderEditor::_mode_selected(int p_id) {
_update_options_menu();
_update_graph();
@@ -1175,6 +1227,7 @@ void VisualShaderEditor::_bind_methods() {
ClassDB::bind_method("_node_selected", &VisualShaderEditor::_node_selected);
ClassDB::bind_method("_scroll_changed", &VisualShaderEditor::_scroll_changed);
ClassDB::bind_method("_delete_request", &VisualShaderEditor::_delete_request);
+ ClassDB::bind_method("_on_nodes_delete", &VisualShaderEditor::_on_nodes_delete);
ClassDB::bind_method("_node_changed", &VisualShaderEditor::_node_changed);
ClassDB::bind_method("_edit_port_default_input", &VisualShaderEditor::_edit_port_default_input);
ClassDB::bind_method("_port_edited", &VisualShaderEditor::_port_edited);
@@ -1224,6 +1277,7 @@ VisualShaderEditor::VisualShaderEditor() {
graph->connect("node_selected", this, "_node_selected");
graph->connect("scroll_offset_changed", this, "_scroll_changed");
graph->connect("duplicate_nodes_request", this, "_duplicate_nodes");
+ graph->connect("delete_nodes_request", this, "_on_nodes_delete");
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_SCALAR);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_VECTOR);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_BOOLEAN);
@@ -1346,9 +1400,10 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("ColorUniform", "Color", "Variables", "VisualShaderNodeColorUniform", TTR("Color uniform."), -1, VisualShaderNode::PORT_TYPE_COLOR));
// BOOLEAN
-
- add_options.push_back(AddOption("BooleanConstant", "Boolean", "Variables", "VisualShaderNodeBooleanConstant", TTR("Boolean constant."), -1, VisualShaderNode::PORT_TYPE_BOOLEAN));
- add_options.push_back(AddOption("BooleanUniform", "Boolean", "Variables", "VisualShaderNodeBooleanUniform", TTR("Boolean uniform."), -1, VisualShaderNode::PORT_TYPE_BOOLEAN));
+ add_options.push_back(AddOption("If", "Conditional", "Functions", "VisualShaderNodeIf", TTR("Returns an associated vector if the provided scalars are equal, greater or less."), -1, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Switch", "Conditional", "Functions", "VisualShaderNodeSwitch", TTR("Returns an associated vector if the provided boolean value is true or false."), -1, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("BooleanConstant", "Conditional", "Variables", "VisualShaderNodeBooleanConstant", TTR("Boolean constant."), -1, VisualShaderNode::PORT_TYPE_BOOLEAN));
+ add_options.push_back(AddOption("BooleanUniform", "Conditional", "Variables", "VisualShaderNodeBooleanUniform", TTR("Boolean uniform."), -1, VisualShaderNode::PORT_TYPE_BOOLEAN));
// INPUT
diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h
index 2709d72931..4b0b48ad92 100644
--- a/editor/plugins/visual_shader_editor_plugin.h
+++ b/editor/plugins/visual_shader_editor_plugin.h
@@ -143,6 +143,7 @@ class VisualShaderEditor : public VBoxContainer {
void _node_selected(Object *p_node);
void _delete_request(int);
+ void _on_nodes_delete();
void _removed_from_graph();
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 752c1afff2..bd245d5da9 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -1741,7 +1741,7 @@ void SceneTreeDock::_update_script_button() {
button_clear_script->show();
}
} else {
- button_create_script->show();
+ button_create_script->hide();
Array selection = editor_selection->get_selected_nodes();
for (int i = 0; i < selection.size(); i++) {
Node *n = Object::cast_to<Node>(selection[i]);
@@ -2208,17 +2208,20 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
if (profile_allow_script_editing) {
- if (!existing_script.is_valid()) {
- menu->add_icon_shortcut(get_icon("ScriptCreate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/attach_script"), TOOL_ATTACH_SCRIPT);
+ if (selection.size() == 1) {
+ if (!existing_script.is_valid()) {
+ menu->add_icon_shortcut(get_icon("ScriptCreate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/attach_script"), TOOL_ATTACH_SCRIPT);
+ } else {
+ menu->add_icon_shortcut(get_icon("ScriptExtend", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/extend_script"), TOOL_ATTACH_SCRIPT);
+ }
}
if (selection.size() > 1 || existing_script.is_valid()) {
menu->add_icon_shortcut(get_icon("ScriptRemove", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/clear_script"), TOOL_CLEAR_SCRIPT);
- menu->add_icon_shortcut(get_icon("ScriptExtend", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/extend_script"), TOOL_ATTACH_SCRIPT);
}
+ menu->add_separator();
}
if (profile_allow_editing) {
- menu->add_separator();
if (selection.size() == 1) {
menu->add_icon_shortcut(get_icon("Rename", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/rename"), TOOL_RENAME);
}
@@ -2230,12 +2233,12 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
menu->add_icon_shortcut(get_icon("MoveDown", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/move_down"), TOOL_MOVE_DOWN);
menu->add_icon_shortcut(get_icon("Duplicate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/duplicate"), TOOL_DUPLICATE);
menu->add_icon_shortcut(get_icon("Reparent", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/reparent"), TOOL_REPARENT);
+ menu->add_icon_shortcut(get_icon("NewRoot", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/make_root"), TOOL_MAKE_ROOT);
}
}
if (selection.size() == 1) {
if (profile_allow_editing) {
- menu->add_icon_shortcut(get_icon("NewRoot", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/make_root"), TOOL_MAKE_ROOT);
menu->add_separator();
menu->add_icon_shortcut(get_icon("Blend", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/merge_from_scene"), TOOL_MERGE_FROM_SCENE);
menu->add_icon_shortcut(get_icon("CreateNewSceneFrom", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/save_branch_as_scene"), TOOL_NEW_SCENE_FROM);
@@ -2277,7 +2280,7 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
menu->add_icon_shortcut(get_icon("Rename", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/batch_rename"), TOOL_BATCH_RENAME);
}
menu->add_separator();
- menu->add_icon_item(get_icon("Help", "EditorIcons"), TTR("Open documentation"), TOOL_OPEN_DOCUMENTATION);
+ menu->add_icon_item(get_icon("Help", "EditorIcons"), TTR("Open Documentation"), TOOL_OPEN_DOCUMENTATION);
if (profile_allow_editing) {
menu->add_separator();
@@ -2508,7 +2511,6 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel
HBoxContainer *filter_hbc = memnew(HBoxContainer);
filter_hbc->add_constant_override("separate", 0);
- ToolButton *tb;
ED_SHORTCUT("scene_tree/rename", TTR("Rename"));
ED_SHORTCUT("scene_tree/batch_rename", TTR("Batch Rename"), KEY_MASK_CMD | KEY_F2);
@@ -2529,19 +2531,17 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel
ED_SHORTCUT("scene_tree/delete_no_confirm", TTR("Delete (No Confirm)"), KEY_MASK_SHIFT | KEY_DELETE);
ED_SHORTCUT("scene_tree/delete", TTR("Delete"), KEY_DELETE);
- tb = memnew(ToolButton);
- tb->connect("pressed", this, "_tool_selected", make_binds(TOOL_NEW, false));
- tb->set_tooltip(TTR("Add/Create a New Node"));
- tb->set_shortcut(ED_GET_SHORTCUT("scene_tree/add_child_node"));
- filter_hbc->add_child(tb);
- button_add = tb;
+ button_add = memnew(ToolButton);
+ button_add->connect("pressed", this, "_tool_selected", make_binds(TOOL_NEW, false));
+ button_add->set_tooltip(TTR("Add/Create a New Node"));
+ button_add->set_shortcut(ED_GET_SHORTCUT("scene_tree/add_child_node"));
+ filter_hbc->add_child(button_add);
- tb = memnew(ToolButton);
- tb->connect("pressed", this, "_tool_selected", make_binds(TOOL_INSTANCE, false));
- tb->set_tooltip(TTR("Instance a scene file as a Node. Creates an inherited scene if no root node exists."));
- tb->set_shortcut(ED_GET_SHORTCUT("scene_tree/instance_scene"));
- filter_hbc->add_child(tb);
- button_instance = tb;
+ button_instance = memnew(ToolButton);
+ button_instance->connect("pressed", this, "_tool_selected", make_binds(TOOL_INSTANCE, false));
+ button_instance->set_tooltip(TTR("Instance a scene file as a Node. Creates an inherited scene if no root node exists."));
+ button_instance->set_shortcut(ED_GET_SHORTCUT("scene_tree/instance_scene"));
+ filter_hbc->add_child(button_instance);
vbc->add_child(filter_hbc);
filter = memnew(LineEdit);
@@ -2551,21 +2551,19 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel
filter->add_constant_override("minimum_spaces", 0);
filter->connect("text_changed", this, "_filter_changed");
- tb = memnew(ToolButton);
- tb->connect("pressed", this, "_tool_selected", make_binds(TOOL_ATTACH_SCRIPT, false));
- tb->set_tooltip(TTR("Attach a new or existing script for the selected node."));
- tb->set_shortcut(ED_GET_SHORTCUT("scene_tree/attach_script"));
- filter_hbc->add_child(tb);
- tb->hide();
- button_create_script = tb;
-
- tb = memnew(ToolButton);
- tb->connect("pressed", this, "_tool_selected", make_binds(TOOL_CLEAR_SCRIPT, false));
- tb->set_tooltip(TTR("Clear a script for the selected node."));
- tb->set_shortcut(ED_GET_SHORTCUT("scene_tree/clear_script"));
- filter_hbc->add_child(tb);
- button_clear_script = tb;
- tb->hide();
+ button_create_script = memnew(ToolButton);
+ button_create_script->connect("pressed", this, "_tool_selected", make_binds(TOOL_ATTACH_SCRIPT, false));
+ button_create_script->set_tooltip(TTR("Attach a new or existing script for the selected node."));
+ button_create_script->set_shortcut(ED_GET_SHORTCUT("scene_tree/attach_script"));
+ filter_hbc->add_child(button_create_script);
+ button_create_script->hide();
+
+ button_clear_script = memnew(ToolButton);
+ button_clear_script->connect("pressed", this, "_tool_selected", make_binds(TOOL_CLEAR_SCRIPT, false));
+ button_clear_script->set_tooltip(TTR("Clear a script for the selected node."));
+ button_clear_script->set_shortcut(ED_GET_SHORTCUT("scene_tree/clear_script"));
+ filter_hbc->add_child(button_clear_script);
+ button_clear_script->hide();
button_hb = memnew(HBoxContainer);
vbc->add_child(button_hb);
diff --git a/modules/assimp/editor_scene_importer_assimp.cpp b/modules/assimp/editor_scene_importer_assimp.cpp
index ee66097ffd..0330ab4604 100644
--- a/modules/assimp/editor_scene_importer_assimp.cpp
+++ b/modules/assimp/editor_scene_importer_assimp.cpp
@@ -113,7 +113,7 @@ Node *EditorSceneImporterAssimp::import_scene(const String &p_path, uint32_t p_f
std::string s_path(w_path.begin(), w_path.end());
importer.SetPropertyBool(AI_CONFIG_PP_FD_REMOVE, true);
// Cannot remove pivot points because the static mesh will be in the wrong place
- importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true);
+ importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, false);
int32_t max_bone_weights = 4;
//if (p_flags & IMPORT_ANIMATION_EIGHT_WEIGHTS) {
// const int eight_bones = 8;
@@ -282,673 +282,438 @@ T EditorSceneImporterAssimp::_interpolate_track(const Vector<float> &p_times, co
ERR_FAIL_V(p_values[0]);
}
-Spatial *EditorSceneImporterAssimp::_generate_scene(const String &p_path, const aiScene *scene, const uint32_t p_flags, int p_bake_fps, const int32_t p_max_bone_weights) {
- ERR_FAIL_COND_V(scene == NULL, NULL);
- Spatial *root = memnew(Spatial);
- AnimationPlayer *ap = NULL;
- if (p_flags & IMPORT_ANIMATION) {
- ap = memnew(AnimationPlayer);
- root->add_child(ap);
- ap->set_owner(root);
- ap->set_name(TTR("AnimationPlayer"));
- }
- Set<String> bone_names;
- Set<String> light_names;
- Set<String> camera_names;
- real_t factor = 1.0f;
- String ext = p_path.get_file().get_extension().to_lower();
- if ((ext == "fbx")) {
- if (scene->mMetaData != NULL) {
- scene->mMetaData->Get("UnitScaleFactor", factor);
- factor = factor * 0.01f;
+void EditorSceneImporterAssimp::_generate_bone_groups(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<String, Transform> &bind_xforms) {
+
+ Transform mesh_offset = _get_global_assimp_node_transform(p_assimp_node);
+ //mesh_offset.basis = Basis();
+ for (uint32_t i = 0; i < p_assimp_node->mNumMeshes; i++) {
+ const aiMesh *mesh = state.assimp_scene->mMeshes[i];
+ int owned_by = -1;
+ for (uint32_t j = 0; j < mesh->mNumBones; j++) {
+ const aiBone *bone = mesh->mBones[j];
+ String name = _assimp_get_string(bone->mName);
+
+ if (ownership.has(name)) {
+ owned_by = ownership[name];
+ break;
+ }
}
- }
- for (size_t l = 0; l < scene->mNumLights; l++) {
- Light *light = NULL;
- aiLight *ai_light = scene->mLights[l];
- ERR_CONTINUE(ai_light == NULL);
- if (ai_light->mType == aiLightSource_DIRECTIONAL) {
- light = memnew(DirectionalLight);
- Vector3 dir = Vector3(ai_light->mDirection.y, ai_light->mDirection.x, ai_light->mDirection.z);
- dir.normalize();
- Transform xform;
- Quat quat;
- quat.set_euler(dir);
- Vector3 pos = Vector3(ai_light->mPosition.x, ai_light->mPosition.y, ai_light->mPosition.z);
- pos = factor * pos;
- xform.origin = pos;
- light->set_transform(xform);
- } else if (ai_light->mType == aiLightSource_POINT) {
- light = memnew(OmniLight);
- Vector3 pos = Vector3(ai_light->mPosition.x, ai_light->mPosition.y, ai_light->mPosition.z);
- Transform xform;
- xform.origin = pos;
- pos = factor * pos;
- light->set_transform(xform);
- // No idea for energy
- light->set_param(Light::PARAM_ATTENUATION, 0.0f);
- } else if (ai_light->mType == aiLightSource_SPOT) {
- light = memnew(SpotLight);
- Vector3 pos = Vector3(ai_light->mPosition.x, ai_light->mPosition.y, ai_light->mPosition.z);
- pos = factor * pos;
- Transform xform;
- xform.origin = pos;
- Vector3 dir = Vector3(ai_light->mDirection.y, ai_light->mDirection.x, ai_light->mDirection.z);
- dir.normalize();
- Quat quat;
- quat.set_euler(dir);
- xform.basis = quat;
- light->set_transform(xform);
- // No idea for energy
- light->set_param(Light::PARAM_ATTENUATION, 0.0f);
+
+ if (owned_by == -1) { //no owned, create new unique id
+ owned_by = 1;
+ for (Map<String, int>::Element *E = ownership.front(); E; E = E->next()) {
+ owned_by = MAX(E->get() + 1, owned_by);
+ }
}
- ERR_CONTINUE(light == NULL);
- light->set_color(Color(ai_light->mColorDiffuse.r, ai_light->mColorDiffuse.g, ai_light->mColorDiffuse.b));
- root->add_child(light);
- light->set_name(_ai_string_to_string(ai_light->mName));
- light->set_owner(root);
- light_names.insert(_ai_string_to_string(scene->mLights[l]->mName));
- }
- for (size_t c = 0; c < scene->mNumCameras; c++) {
- aiCamera *ai_camera = scene->mCameras[c];
- Camera *camera = memnew(Camera);
- float near = ai_camera->mClipPlaneNear;
- if (Math::is_equal_approx(near, 0.0f)) {
- near = 0.1f;
+
+ for (uint32_t j = 0; j < mesh->mNumBones; j++) {
+ const aiBone *bone = mesh->mBones[j];
+ String name = _assimp_get_string(bone->mName);
+ ownership[name] = owned_by;
+ //store the actuall full path for the bone transform
+ //when skeleton finds it's place in the tree, it will be restored
+ bind_xforms[name] = mesh_offset * _assimp_matrix_transform(bone->mOffsetMatrix);
}
- camera->set_perspective(Math::rad2deg(ai_camera->mHorizontalFOV) * 2.0f, near, ai_camera->mClipPlaneFar);
- Vector3 pos = Vector3(ai_camera->mPosition.x, ai_camera->mPosition.y, ai_camera->mPosition.z);
+ }
- Vector3 look_at = Vector3(ai_camera->mLookAt.y, ai_camera->mLookAt.x, ai_camera->mLookAt.z).normalized();
- Quat quat;
- quat.set_euler(look_at);
- Transform xform;
- xform.basis = quat;
- xform.set_origin(pos);
- root->add_child(camera);
- camera->set_transform(xform);
- camera->set_name(_ai_string_to_string(ai_camera->mName));
- camera->set_owner(root);
- camera_names.insert(_ai_string_to_string(scene->mCameras[c]->mName));
- }
- Map<Skeleton *, MeshInstance *> skeletons;
- Map<String, Transform> bone_rests;
- Vector<MeshInstance *> meshes;
- int32_t mesh_count = 0;
- Skeleton *s = memnew(Skeleton);
- Set<String> removed_bones;
- Map<String, Map<uint32_t, String> > path_morph_mesh_names;
- _generate_node(p_path, scene, scene->mRootNode, root, root, bone_names, light_names, camera_names, skeletons, bone_rests, meshes, mesh_count, s, p_max_bone_weights, removed_bones, path_morph_mesh_names);
- for (Map<Skeleton *, MeshInstance *>::Element *E = skeletons.front(); E; E = E->next()) {
- E->key()->localize_rests();
- }
- Set<String> removed_nodes;
- Set<Node *> keep_nodes;
- _keep_node(p_path, root, root, keep_nodes);
- _fill_kept_node(keep_nodes);
- _filter_node(p_path, root, root, keep_nodes, removed_nodes);
- if (p_flags & IMPORT_ANIMATION) {
- for (size_t i = 0; i < scene->mNumAnimations; i++) {
- _import_animation(p_path, meshes, scene, ap, i, p_bake_fps, skeletons, removed_nodes, removed_bones, path_morph_mesh_names);
- }
- List<StringName> animation_names;
- ap->get_animation_list(&animation_names);
- if (animation_names.empty()) {
- root->remove_child(ap);
- memdelete(ap);
- }
- }
- return root;
+ for (size_t i = 0; i < p_assimp_node->mNumChildren; i++) {
+ _generate_bone_groups(state, p_assimp_node->mChildren[i], ownership, bind_xforms);
+ }
}
-void EditorSceneImporterAssimp::_fill_kept_node(Set<Node *> &keep_nodes) {
- for (Set<Node *>::Element *E = keep_nodes.front(); E; E = E->next()) {
- Node *node = E->get();
- while (node != NULL) {
- if (keep_nodes.has(node) == false) {
- keep_nodes.insert(node);
+void EditorSceneImporterAssimp::_fill_node_relationships(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<int, int> &skeleton_map, int p_skeleton_id, Skeleton *p_skeleton, const String &p_parent_name, int &holecount, const Vector<SkeletonHole> &p_holes, const Map<String, Transform> &bind_xforms) {
+
+ String name = _assimp_get_string(p_assimp_node->mName);
+ if (name == String()) {
+ name = "AuxiliaryBone" + itos(holecount++);
+ }
+
+ Transform pose = _assimp_matrix_transform(p_assimp_node->mTransformation);
+
+ if (!ownership.has(name)) {
+ //not a bone, it's a hole
+ Vector<SkeletonHole> holes = p_holes;
+ SkeletonHole hole; //add a new one
+ hole.name = name;
+ hole.pose = pose;
+ hole.node = p_assimp_node;
+ hole.parent = p_parent_name;
+ holes.push_back(hole);
+
+ for (size_t i = 0; i < p_assimp_node->mNumChildren; i++) {
+ _fill_node_relationships(state, p_assimp_node->mChildren[i], ownership, skeleton_map, p_skeleton_id, p_skeleton, name, holecount, holes, bind_xforms);
+ }
+
+ return;
+ } else if (ownership[name] != p_skeleton_id) {
+ //oh, it's from another skeleton? fine.. reparent all bones to this skeleton.
+ int prev_owner = ownership[name];
+ ERR_EXPLAIN("A previous skeleton exists for bone '" + name + "', this type of skeleton layout is unsupported.");
+ ERR_FAIL_COND(skeleton_map.has(prev_owner));
+ for (Map<String, int>::Element *E = ownership.front(); E; E = E->next()) {
+ if (E->get() == prev_owner) {
+ E->get() = p_skeleton_id;
}
- node = node->get_parent();
}
}
-}
-String EditorSceneImporterAssimp::_find_skeleton_bone_root(Map<Skeleton *, MeshInstance *> &skeletons, Map<MeshInstance *, String> &meshes, Spatial *root) {
- for (Map<Skeleton *, MeshInstance *>::Element *E = skeletons.front(); E; E = E->next()) {
- if (meshes.has(E->get())) {
- String name = meshes[E->get()];
- if (name != "") {
- return name;
- }
+ //valid bone, first fill holes if needed
+ for (int i = 0; i < p_holes.size(); i++) {
+
+ int bone_idx = p_skeleton->get_bone_count();
+ p_skeleton->add_bone(p_holes[i].name);
+ int parent_idx = p_skeleton->find_bone(p_holes[i].parent);
+ if (parent_idx >= 0) {
+ p_skeleton->set_bone_parent(bone_idx, parent_idx);
}
+
+ Transform pose_transform = _get_global_assimp_node_transform(p_holes[i].node);
+ p_skeleton->set_bone_rest(bone_idx, pose_transform);
+
+ state.bone_owners[p_holes[i].name] = skeleton_map[p_skeleton_id];
+ }
+
+ //finally fill bone
+
+ int bone_idx = p_skeleton->get_bone_count();
+ p_skeleton->add_bone(name);
+ int parent_idx = p_skeleton->find_bone(p_parent_name);
+ if (parent_idx >= 0) {
+ p_skeleton->set_bone_parent(bone_idx, parent_idx);
+ }
+ //p_skeleton->set_bone_pose(bone_idx, pose);
+ if (bind_xforms.has(name)) {
+ //for now this is the full path to the bone in rest pose
+ //when skeleton finds it's place in the tree, it will get fixed
+ p_skeleton->set_bone_rest(bone_idx, bind_xforms[name]);
+ }
+ state.bone_owners[name] = skeleton_map[p_skeleton_id];
+ //go to children
+ for (size_t i = 0; i < p_assimp_node->mNumChildren; i++) {
+ _fill_node_relationships(state, p_assimp_node->mChildren[i], ownership, skeleton_map, p_skeleton_id, p_skeleton, name, holecount, Vector<SkeletonHole>(), bind_xforms);
}
- return "";
}
-void EditorSceneImporterAssimp::_set_bone_parent(Skeleton *s, Node *p_owner, aiNode *p_node) {
- for (int32_t j = 0; j < s->get_bone_count(); j++) {
- String bone_name = s->get_bone_name(j);
- const aiNode *ai_bone_node = _ai_find_node(p_node, bone_name);
- if (ai_bone_node == NULL) {
- continue;
- }
- ai_bone_node = ai_bone_node->mParent;
- while (ai_bone_node != NULL) {
- int32_t node_parent_index = -1;
- String parent_bone_name = _ai_string_to_string(ai_bone_node->mName);
- node_parent_index = s->find_bone(parent_bone_name);
- if (node_parent_index != -1) {
- s->set_bone_parent(j, node_parent_index);
- break;
+void EditorSceneImporterAssimp::_generate_skeletons(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<int, int> &skeleton_map, const Map<String, Transform> &bind_xforms) {
+
+ //find skeletons at this level, there may be multiple root nodes for each
+ Map<int, List<aiNode *> > skeletons_found;
+ for (size_t i = 0; i < p_assimp_node->mNumChildren; i++) {
+ String name = _assimp_get_string(p_assimp_node->mChildren[i]->mName);
+ if (ownership.has(name)) {
+ int skeleton = ownership[name];
+ if (!skeletons_found.has(skeleton)) {
+ skeletons_found[skeleton] = List<aiNode *>();
}
- ai_bone_node = ai_bone_node->mParent;
+ skeletons_found[skeleton].push_back(p_assimp_node->mChildren[i]);
}
}
+
+ //go via the potential skeletons found and generate the actual skeleton
+ for (Map<int, List<aiNode *> >::Element *E = skeletons_found.front(); E; E = E->next()) {
+ ERR_CONTINUE(skeleton_map.has(E->key())); //skeleton already exists? this can't be.. skip
+ Skeleton *skeleton = memnew(Skeleton);
+ //this the only way to reliably use multiple meshes with one skeleton, at the cost of less precision
+ skeleton->set_use_bones_in_world_transform(true);
+ skeleton_map[E->key()] = state.skeletons.size();
+ state.skeletons.push_back(skeleton);
+ int holecount = 1;
+ //fill the bones and their relationships
+ for (List<aiNode *>::Element *F = E->get().front(); F; F = F->next()) {
+ _fill_node_relationships(state, F->get(), ownership, skeleton_map, E->key(), skeleton, "", holecount, Vector<SkeletonHole>(), bind_xforms);
+ }
+ }
+
+ //go to the children
+ for (uint32_t i = 0; i < p_assimp_node->mNumChildren; i++) {
+ String name = _assimp_get_string(p_assimp_node->mChildren[i]->mName);
+ if (ownership.has(name)) {
+ continue; //a bone, so don't bother with this
+ }
+ _generate_skeletons(state, p_assimp_node->mChildren[i], ownership, skeleton_map, bind_xforms);
+ }
}
-void EditorSceneImporterAssimp::_insert_animation_track(const aiScene *p_scene, const String p_path, int p_bake_fps, Ref<Animation> animation, float ticks_per_second, float length, const Skeleton *sk, const aiNodeAnim *track, String node_name, NodePath node_path) {
+Spatial *EditorSceneImporterAssimp::_generate_scene(const String &p_path, const aiScene *scene, const uint32_t p_flags, int p_bake_fps, const int32_t p_max_bone_weights) {
+ ERR_FAIL_COND_V(scene == NULL, NULL);
- if (track->mNumRotationKeys || track->mNumPositionKeys || track->mNumScalingKeys) {
- //make transform track
- int track_idx = animation->get_track_count();
- animation->add_track(Animation::TYPE_TRANSFORM);
- animation->track_set_path(track_idx, node_path);
- //first determine animation length
+ ImportState state;
+ state.path = p_path;
+ state.assimp_scene = scene;
+ state.max_bone_weights = p_max_bone_weights;
+ state.root = memnew(Spatial);
+ state.fbx = false;
+ state.animation_player = NULL;
- for (size_t i = 0; i < track->mNumRotationKeys; i++) {
- length = MAX(length, track->mRotationKeys[i].mTime / ticks_per_second);
- }
- for (size_t i = 0; i < track->mNumPositionKeys; i++) {
- length = MAX(length, track->mPositionKeys[i].mTime / ticks_per_second);
- }
- for (size_t i = 0; i < track->mNumScalingKeys; i++) {
- length = MAX(length, track->mScalingKeys[i].mTime / ticks_per_second);
+ real_t scale_factor = 1.0f;
+ {
+ //handle scale
+ String ext = p_path.get_file().get_extension().to_lower();
+ if (ext == "fbx") {
+ if (scene->mMetaData != NULL) {
+ float factor = 1.0;
+ scene->mMetaData->Get("UnitScaleFactor", factor);
+ scale_factor = factor * 0.01f;
+ }
+ state.fbx = true;
}
+ }
- float increment = 1.0 / float(p_bake_fps);
- float time = 0.0;
+ state.root->set_scale(Vector3(scale_factor, scale_factor, scale_factor));
- Vector3 base_pos;
- Quat base_rot;
- Vector3 base_scale = Vector3(1, 1, 1);
+ //fill light map cache
+ for (size_t l = 0; l < scene->mNumLights; l++) {
- if (track->mNumRotationKeys != 0) {
- aiQuatKey key = track->mRotationKeys[0];
- real_t x = key.mValue.x;
- real_t y = key.mValue.y;
- real_t z = key.mValue.z;
- real_t w = key.mValue.w;
- Quat q(x, y, z, w);
- q = q.normalized();
- base_rot = q;
- }
+ aiLight *ai_light = scene->mLights[l];
+ ERR_CONTINUE(ai_light == NULL);
+ state.light_cache[_assimp_get_string(ai_light->mName)] = l;
+ }
- if (track->mNumPositionKeys != 0) {
- aiVectorKey key = track->mPositionKeys[0];
- real_t x = key.mValue.x;
- real_t y = key.mValue.y;
- real_t z = key.mValue.z;
- base_pos = Vector3(x, y, z);
+ //fill camera cache
+ for (size_t c = 0; c < scene->mNumCameras; c++) {
+ aiCamera *ai_camera = scene->mCameras[c];
+ ERR_CONTINUE(ai_camera == NULL);
+ state.camera_cache[_assimp_get_string(ai_camera->mName)] = c;
+ }
+
+ if (scene->mRootNode) {
+ Map<String, Transform> bind_xforms; //temporary map to store bind transforms
+ //guess the skeletons, since assimp does not really support them directly
+ Map<String, int> ownership; //bone names to groups
+ //fill this map with bone names and which group where they detected to, going mesh by mesh
+ _generate_bone_groups(state, state.assimp_scene->mRootNode, ownership, bind_xforms);
+ Map<int, int> skeleton_map; //maps previously created groups to actual skeletons
+ //generates the skeletons when bones are found in the hierarchy, and follows them (including gaps/holes).
+ _generate_skeletons(state, state.assimp_scene->mRootNode, ownership, skeleton_map, bind_xforms);
+
+ //generate nodes
+ for (uint32_t i = 0; i < scene->mRootNode->mNumChildren; i++) {
+ _generate_node(state, scene->mRootNode->mChildren[i], state.root);
}
- if (track->mNumScalingKeys != 0) {
- aiVectorKey key = track->mScalingKeys[0];
- real_t x = key.mValue.x;
- real_t y = key.mValue.y;
- real_t z = key.mValue.z;
- base_scale = Vector3(x, y, z);
+ //assign skeletons to nodes
+
+ for (Map<MeshInstance *, Skeleton *>::Element *E = state.mesh_skeletons.front(); E; E = E->next()) {
+ MeshInstance *mesh = E->key();
+ Skeleton *skeleton = E->get();
+ NodePath skeleton_path = mesh->get_path_to(skeleton);
+ mesh->set_skeleton_path(skeleton_path);
}
+ }
- bool last = false;
+ if (p_flags & IMPORT_ANIMATION && scene->mNumAnimations) {
- Vector<Vector3> pos_values;
- Vector<float> pos_times;
- Vector<Vector3> scale_values;
- Vector<float> scale_times;
- Vector<Quat> rot_values;
- Vector<float> rot_times;
+ state.animation_player = memnew(AnimationPlayer);
+ state.root->add_child(state.animation_player);
+ state.animation_player->set_owner(state.root);
- for (size_t p = 0; p < track->mNumPositionKeys; p++) {
- aiVector3D pos = track->mPositionKeys[p].mValue;
- pos_values.push_back(Vector3(pos.x, pos.y, pos.z));
- pos_times.push_back(track->mPositionKeys[p].mTime / ticks_per_second);
+ for (uint32_t i = 0; i < scene->mNumAnimations; i++) {
+ _import_animation(state, i, p_bake_fps);
}
+ }
+
+ return state.root;
+}
+
+void EditorSceneImporterAssimp::_insert_animation_track(ImportState &scene, const aiAnimation *assimp_anim, int p_track, int p_bake_fps, Ref<Animation> animation, float ticks_per_second, Skeleton *p_skeleton, const NodePath &p_path, const String &p_name) {
+
+ const aiNodeAnim *assimp_track = assimp_anim->mChannels[p_track];
+ //make transform track
+ int track_idx = animation->get_track_count();
+ animation->add_track(Animation::TYPE_TRANSFORM);
+ animation->track_set_path(track_idx, p_path);
+ //first determine animation length
- for (size_t r = 0; r < track->mNumRotationKeys; r++) {
- aiQuaternion quat = track->mRotationKeys[r].mValue;
- rot_values.push_back(Quat(quat.x, quat.y, quat.z, quat.w).normalized());
- rot_times.push_back(track->mRotationKeys[r].mTime / ticks_per_second);
+ float increment = 1.0 / float(p_bake_fps);
+ float time = 0.0;
+
+ bool last = false;
+
+ int skeleton_bone = -1;
+
+ if (p_skeleton) {
+ skeleton_bone = p_skeleton->find_bone(p_name);
+ }
+
+ Vector<Vector3> pos_values;
+ Vector<float> pos_times;
+ Vector<Vector3> scale_values;
+ Vector<float> scale_times;
+ Vector<Quat> rot_values;
+ Vector<float> rot_times;
+
+ for (size_t p = 0; p < assimp_track->mNumPositionKeys; p++) {
+ aiVector3D pos = assimp_track->mPositionKeys[p].mValue;
+ pos_values.push_back(Vector3(pos.x, pos.y, pos.z));
+ pos_times.push_back(assimp_track->mPositionKeys[p].mTime / ticks_per_second);
+ }
+
+ for (size_t r = 0; r < assimp_track->mNumRotationKeys; r++) {
+ aiQuaternion quat = assimp_track->mRotationKeys[r].mValue;
+ rot_values.push_back(Quat(quat.x, quat.y, quat.z, quat.w).normalized());
+ rot_times.push_back(assimp_track->mRotationKeys[r].mTime / ticks_per_second);
+ }
+
+ for (size_t sc = 0; sc < assimp_track->mNumScalingKeys; sc++) {
+ aiVector3D scale = assimp_track->mScalingKeys[sc].mValue;
+ scale_values.push_back(Vector3(scale.x, scale.y, scale.z));
+ scale_times.push_back(assimp_track->mScalingKeys[sc].mTime / ticks_per_second);
+ }
+ while (true) {
+ Vector3 pos;
+ Quat rot;
+ Vector3 scale(1, 1, 1);
+
+ if (pos_values.size()) {
+ pos = _interpolate_track<Vector3>(pos_times, pos_values, time, AssetImportAnimation::INTERP_LINEAR);
}
- for (size_t sc = 0; sc < track->mNumScalingKeys; sc++) {
- aiVector3D scale = track->mScalingKeys[sc].mValue;
- scale_values.push_back(Vector3(scale.x, scale.y, scale.z));
- scale_times.push_back(track->mScalingKeys[sc].mTime / ticks_per_second);
+ if (rot_values.size()) {
+ rot = _interpolate_track<Quat>(rot_times, rot_values, time, AssetImportAnimation::INTERP_LINEAR).normalized();
}
- while (true) {
- Vector3 pos = base_pos;
- Quat rot = base_rot;
- Vector3 scale = base_scale;
- if (pos_values.size()) {
- pos = _interpolate_track<Vector3>(pos_times, pos_values, time, AssetImportAnimation::INTERP_LINEAR);
- }
+ if (scale_values.size()) {
+ scale = _interpolate_track<Vector3>(scale_times, scale_values, time, AssetImportAnimation::INTERP_LINEAR);
+ }
- if (rot_values.size()) {
- rot = _interpolate_track<Quat>(rot_times, rot_values, time, AssetImportAnimation::INTERP_LINEAR).normalized();
- }
+ if (skeleton_bone >= 0) {
+ Transform xform;
+ xform.basis.set_quat_scale(rot, scale);
+ xform.origin = pos;
- if (scale_values.size()) {
- scale = _interpolate_track<Vector3>(scale_times, scale_values, time, AssetImportAnimation::INTERP_LINEAR);
- }
+ Transform rest_xform = p_skeleton->get_bone_rest(skeleton_bone);
+ xform = rest_xform.affine_inverse() * xform;
+ rot = xform.basis.get_rotation_quat();
+ scale = xform.basis.get_scale();
+ pos = xform.origin;
+ }
- if (sk != NULL && sk->find_bone(node_name) != -1) {
- Transform xform;
- xform.basis.set_quat_scale(rot, scale);
- xform.origin = pos;
-
- int bone = sk->find_bone(node_name);
- Transform rest_xform = sk->get_bone_rest(bone);
- xform = rest_xform.affine_inverse() * xform;
- rot = xform.basis.get_rotation_quat();
- scale = xform.basis.get_scale();
- pos = xform.origin;
- }
- {
- Transform xform;
- xform.basis.set_quat_scale(rot, scale);
- xform.origin = pos;
- Transform anim_xform;
- String ext = p_path.get_file().get_extension().to_lower();
- if (ext == "fbx") {
- real_t factor = 1.0f;
- if (p_scene->mMetaData != NULL) {
- p_scene->mMetaData->Get("UnitScaleFactor", factor);
- }
- anim_xform = anim_xform.scaled(Vector3(factor, factor, factor));
- }
- xform = anim_xform * xform;
- rot = xform.basis.get_rotation_quat();
- scale = xform.basis.get_scale();
- pos = xform.origin;
- }
- rot.normalize();
+ rot.normalize();
- animation->track_set_interpolation_type(track_idx, Animation::INTERPOLATION_LINEAR);
- animation->transform_track_insert_key(track_idx, time, pos, rot, scale);
+ animation->track_set_interpolation_type(track_idx, Animation::INTERPOLATION_LINEAR);
+ animation->transform_track_insert_key(track_idx, time, pos, rot, scale);
- if (last) {
- break;
- }
- time += increment;
- if (time >= length) {
- last = true;
- time = length;
- }
+ if (last) { //done this way so a key is always inserted past the end (for proper interpolation)
+ break;
+ }
+ time += increment;
+ if (time >= animation->get_length()) {
+ last = true;
}
}
}
-void EditorSceneImporterAssimp::_import_animation(const String p_path, const Vector<MeshInstance *> p_meshes, const aiScene *p_scene, AnimationPlayer *ap, int32_t p_index, int p_bake_fps, Map<Skeleton *, MeshInstance *> p_skeletons, const Set<String> p_removed_nodes, const Set<String> removed_bones, const Map<String, Map<uint32_t, String> > p_path_morph_mesh_names) {
- String name = "Animation";
- aiAnimation const *anim = NULL;
- if (p_index != -1) {
- anim = p_scene->mAnimations[p_index];
- if (anim->mName.length > 0) {
- name = _ai_anim_string_to_string(anim->mName);
- }
+void EditorSceneImporterAssimp::_import_animation(ImportState &state, int p_animation_index, int p_bake_fps) {
+
+ ERR_FAIL_INDEX(p_animation_index, (int)state.assimp_scene->mNumAnimations);
+
+ const aiAnimation *anim = state.assimp_scene->mAnimations[p_animation_index];
+ String name = _assimp_anim_string_to_string(anim->mName);
+ if (name == String()) {
+ name = "Animation " + itos(p_animation_index + 1);
}
- Ref<Animation> animation;
- animation.instance();
- float length = 0.0f;
- animation->set_name(name);
- float ticks_per_second = p_scene->mAnimations[p_index]->mTicksPerSecond;
+ float ticks_per_second = anim->mTicksPerSecond;
- if (p_scene->mMetaData != NULL && Math::is_equal_approx(ticks_per_second, 0.0f)) {
+ if (state.assimp_scene->mMetaData != NULL && Math::is_equal_approx(ticks_per_second, 0.0f)) {
int32_t time_mode = 0;
- p_scene->mMetaData->Get("TimeMode", time_mode);
- ticks_per_second = _get_fbx_fps(time_mode, p_scene);
+ state.assimp_scene->mMetaData->Get("TimeMode", time_mode);
+ ticks_per_second = _get_fbx_fps(time_mode, state.assimp_scene);
}
- if ((p_path.get_file().get_extension().to_lower() == "glb" || p_path.get_file().get_extension().to_lower() == "gltf") && Math::is_equal_approx(ticks_per_second, 0.0f)) {
- ticks_per_second = 1000.0f;
- }
+ //?
+ //if ((p_path.get_file().get_extension().to_lower() == "glb" || p_path.get_file().get_extension().to_lower() == "gltf") && Math::is_equal_approx(ticks_per_second, 0.0f)) {
+ // ticks_per_second = 1000.0f;
+ //}
if (Math::is_equal_approx(ticks_per_second, 0.0f)) {
ticks_per_second = 25.0f;
}
- length = anim->mDuration / ticks_per_second;
- if (anim) {
- Map<String, Vector<const aiNodeAnim *> > node_tracks;
- for (size_t i = 0; i < anim->mNumChannels; i++) {
- const aiNodeAnim *track = anim->mChannels[i];
- String node_name = _ai_string_to_string(track->mNodeName);
- NodePath node_path = node_name;
- bool is_bone = false;
- if (node_name.split(ASSIMP_FBX_KEY).size() > 1) {
- String p_track_type = node_name.split(ASSIMP_FBX_KEY)[1];
- if (p_track_type == "_Translation" || p_track_type == "_Rotation" || p_track_type == "_Scaling") {
- continue;
- }
- }
- for (Map<Skeleton *, MeshInstance *>::Element *E = p_skeletons.front(); E; E = E->next()) {
- Skeleton *sk = E->key();
- const String path = ap->get_owner()->get_path_to(sk);
- if (path.empty()) {
- continue;
- }
- if (sk->find_bone(node_name) == -1) {
- continue;
- }
- node_path = path + ":" + node_name;
- ERR_CONTINUE(ap->get_owner()->has_node(node_path) == false);
- _insert_animation_track(p_scene, p_path, p_bake_fps, animation, ticks_per_second, length, sk, track, node_name, node_path);
- is_bone = true;
- }
- if (is_bone) {
- continue;
- }
- Node *node = ap->get_owner()->find_node(node_name);
- if (node == NULL) {
- continue;
- }
- if (p_removed_nodes.has(node_name)) {
- continue;
- }
- const String path = ap->get_owner()->get_path_to(node);
- if (path.empty()) {
- print_verbose("Can't animate path");
- continue;
- }
- node_path = path;
- if (ap->get_owner()->has_node(node_path) == false) {
- continue;
- }
- _insert_animation_track(p_scene, p_path, p_bake_fps, animation, ticks_per_second, length, NULL, track, node_name, node_path);
- }
- for (size_t i = 0; i < anim->mNumChannels; i++) {
- const aiNodeAnim *track = anim->mChannels[i];
- String node_name = _ai_string_to_string(track->mNodeName);
- Vector<String> split_name = node_name.split(ASSIMP_FBX_KEY);
- String bare_name = split_name[0];
- Node *node = ap->get_owner()->find_node(bare_name);
- if (node != NULL && split_name.size() > 1) {
- Map<String, Vector<const aiNodeAnim *> >::Element *E = node_tracks.find(bare_name);
- Vector<const aiNodeAnim *> ai_tracks;
- if (E) {
- ai_tracks = E->get();
- ai_tracks.push_back(anim->mChannels[i]);
- } else {
- ai_tracks.push_back(anim->mChannels[i]);
- }
- node_tracks.insert(bare_name, ai_tracks);
- }
- }
- for (Map<Skeleton *, MeshInstance *>::Element *E = p_skeletons.front(); E; E = E->next()) {
- Skeleton *sk = E->key();
- Map<String, Vector<const aiNodeAnim *> > anim_tracks;
- for (int32_t i = 0; i < sk->get_bone_count(); i++) {
- String _bone_name = sk->get_bone_name(i);
- Vector<const aiNodeAnim *> ai_tracks;
+ Ref<Animation> animation;
+ animation.instance();
+ animation->set_name(name);
+ animation->set_length(anim->mDuration / ticks_per_second);
- if (sk->find_bone(_bone_name) == -1) {
- continue;
- }
- for (size_t j = 0; j < anim->mNumChannels; j++) {
- if (_ai_string_to_string(anim->mChannels[j]->mNodeName).split(ASSIMP_FBX_KEY).size() == 1) {
- continue;
- }
- String track_name = _ai_string_to_string(anim->mChannels[j]->mNodeName).split(ASSIMP_FBX_KEY)[0];
- if (track_name != _bone_name) {
- continue;
- }
- if (sk->find_bone(_bone_name) == -1) {
- continue;
- }
- ai_tracks.push_back(anim->mChannels[j]);
- }
- if (ai_tracks.size() == 0) {
- continue;
- }
- anim_tracks.insert(_bone_name, ai_tracks);
- }
- for (Map<String, Vector<const aiNodeAnim *> >::Element *F = anim_tracks.front(); F; F = F->next()) {
- _insert_pivot_anim_track(p_meshes, F->key(), F->get(), ap, sk, length, ticks_per_second, animation, p_bake_fps, p_path, p_scene);
- }
- }
- for (Map<String, Vector<const aiNodeAnim *> >::Element *E = node_tracks.front(); E; E = E->next()) {
- if (p_removed_nodes.has(E->key())) {
- continue;
- }
- if (removed_bones.find(E->key())) {
+ //regular tracks
+
+ for (size_t i = 0; i < anim->mNumChannels; i++) {
+ const aiNodeAnim *track = anim->mChannels[i];
+ String node_name = _assimp_get_string(track->mNodeName);
+ /*
+ if (node_name.find(ASSIMP_FBX_KEY) != -1) {
+ String p_track_type = node_name.get_slice(ASSIMP_FBX_KEY, 1);
+ if (p_track_type == "_Translation" || p_track_type == "_Rotation" || p_track_type == "_Scaling") {
continue;
}
- _insert_pivot_anim_track(p_meshes, E->key(), E->get(), ap, NULL, length, ticks_per_second, animation, p_bake_fps, p_path, p_scene);
}
- for (size_t i = 0; i < anim->mNumMorphMeshChannels; i++) {
- const aiMeshMorphAnim *anim_mesh = anim->mMorphMeshChannels[i];
- const String prop_name = _ai_string_to_string(anim_mesh->mName);
- const String mesh_name = prop_name.split("*")[0];
- if (p_removed_nodes.has(mesh_name)) {
- continue;
- }
- ERR_CONTINUE(prop_name.split("*").size() != 2);
- const MeshInstance *mesh_instance = Object::cast_to<MeshInstance>(ap->get_owner()->find_node(mesh_name));
- ERR_CONTINUE(mesh_instance == NULL);
- if (ap->get_owner()->find_node(mesh_instance->get_name()) == NULL) {
- print_verbose("Can't find mesh in scene: " + mesh_instance->get_name());
- continue;
- }
- const String path = ap->get_owner()->get_path_to(mesh_instance);
- if (path.empty()) {
- print_verbose("Can't find mesh in scene");
- continue;
- }
- Ref<Mesh> mesh = mesh_instance->get_mesh();
- ERR_CONTINUE(mesh.is_null());
- const Map<String, Map<uint32_t, String> >::Element *E = p_path_morph_mesh_names.find(mesh_name);
- ERR_CONTINUE(E == NULL);
- for (size_t k = 0; k < anim_mesh->mNumKeys; k++) {
- for (size_t j = 0; j < anim_mesh->mKeys[k].mNumValuesAndWeights; j++) {
- const Map<uint32_t, String>::Element *F = E->get().find(anim_mesh->mKeys[k].mValues[j]);
- ERR_CONTINUE(F == NULL);
- const String prop = "blend_shapes/" + F->get();
- const NodePath node_path = String(path) + ":" + prop;
- ERR_CONTINUE(ap->get_owner()->has_node(node_path) == false);
- int32_t blend_track_idx = -1;
- if (animation->find_track(node_path) == -1) {
- blend_track_idx = animation->get_track_count();
- animation->add_track(Animation::TYPE_VALUE);
- animation->track_set_interpolation_type(blend_track_idx, Animation::INTERPOLATION_LINEAR);
- animation->track_set_path(blend_track_idx, node_path);
- } else {
- blend_track_idx = animation->find_track(node_path);
- }
- float t = anim_mesh->mKeys[k].mTime / ticks_per_second;
- float w = anim_mesh->mKeys[k].mWeights[j];
- animation->track_insert_key(blend_track_idx, t, w);
- }
- }
+*/
+ if (track->mNumRotationKeys == 0 && track->mNumPositionKeys == 0 && track->mNumScalingKeys == 0) {
+ continue; //do not bother
}
- }
- animation->set_length(length);
- if (animation->get_track_count()) {
- ap->add_animation(name, animation);
- }
-}
-void EditorSceneImporterAssimp::_insert_pivot_anim_track(const Vector<MeshInstance *> p_meshes, const String p_node_name, Vector<const aiNodeAnim *> F, AnimationPlayer *ap, Skeleton *sk, float &length, float ticks_per_second, Ref<Animation> animation, int p_bake_fps, const String &p_path, const aiScene *p_scene) {
- NodePath node_path;
- if (sk != NULL) {
- const String path = ap->get_owner()->get_path_to(sk);
- if (path.empty()) {
- return;
- }
- if (sk->find_bone(p_node_name) == -1) {
- return;
- }
- node_path = path + ":" + p_node_name;
- } else {
- Node *node = ap->get_owner()->find_node(p_node_name);
- if (node == NULL) {
- return;
- }
- const String path = ap->get_owner()->get_path_to(node);
- node_path = path;
- }
- if (node_path.is_empty()) {
- return;
- }
+ bool is_bone = state.bone_owners.has(node_name);
+ NodePath node_path;
+ Skeleton *skeleton = NULL;
- Vector<Vector3> pos_values;
- Vector<float> pos_times;
- Vector<Vector3> scale_values;
- Vector<float> scale_times;
- Vector<Quat> rot_values;
- Vector<float> rot_times;
- Vector3 base_pos;
- Quat base_rot;
- Vector3 base_scale = Vector3(1, 1, 1);
- bool is_translation = false;
- bool is_rotation = false;
- bool is_scaling = false;
- for (int32_t k = 0; k < F.size(); k++) {
- String p_track_type = _ai_string_to_string(F[k]->mNodeName).split(ASSIMP_FBX_KEY)[1];
- if (p_track_type == "_Translation") {
- is_translation = is_translation || true;
- } else if (p_track_type == "_Rotation") {
- is_rotation = is_rotation || true;
- } else if (p_track_type == "_Scaling") {
- is_scaling = is_scaling || true;
+ if (is_bone) {
+ skeleton = state.skeletons[state.bone_owners[node_name]];
+ String path = state.root->get_path_to(skeleton);
+ path += ":" + node_name;
+ node_path = path;
} else {
- continue;
+
+ ERR_CONTINUE(!state.node_map.has(node_name));
+ Node *node = state.node_map[node_name];
+ node_path = state.root->get_path_to(node);
}
- ERR_CONTINUE(ap->get_owner()->has_node(node_path) == false);
- if (F[k]->mNumRotationKeys || F[k]->mNumPositionKeys || F[k]->mNumScalingKeys) {
+ _insert_animation_track(state, anim, i, p_bake_fps, animation, ticks_per_second, skeleton, node_path, node_name);
+ }
- if (is_rotation) {
- for (size_t i = 0; i < F[k]->mNumRotationKeys; i++) {
- length = MAX(length, F[k]->mRotationKeys[i].mTime / ticks_per_second);
- }
- }
- if (is_translation) {
- for (size_t i = 0; i < F[k]->mNumPositionKeys; i++) {
- length = MAX(length, F[k]->mPositionKeys[i].mTime / ticks_per_second);
- }
- }
- if (is_scaling) {
- for (size_t i = 0; i < F[k]->mNumScalingKeys; i++) {
- length = MAX(length, F[k]->mScalingKeys[i].mTime / ticks_per_second);
- }
- }
+ //blend shape tracks
- if (is_rotation == false && is_translation == false && is_scaling == false) {
- return;
- }
+ for (size_t i = 0; i < anim->mNumMorphMeshChannels; i++) {
- if (is_rotation) {
- if (F[k]->mNumRotationKeys != 0) {
- aiQuatKey key = F[k]->mRotationKeys[0];
- real_t x = key.mValue.x;
- real_t y = key.mValue.y;
- real_t z = key.mValue.z;
- real_t w = key.mValue.w;
- Quat q(x, y, z, w);
- q = q.normalized();
- base_rot = q;
- }
- }
+ const aiMeshMorphAnim *anim_mesh = anim->mMorphMeshChannels[i];
- if (is_translation) {
- if (F[k]->mNumPositionKeys != 0) {
- aiVectorKey key = F[k]->mPositionKeys[0];
- real_t x = key.mValue.x;
- real_t y = key.mValue.y;
- real_t z = key.mValue.z;
- base_pos = Vector3(x, y, z);
- }
- }
+ const String prop_name = _assimp_get_string(anim_mesh->mName);
+ const String mesh_name = prop_name.split("*")[0];
- if (is_scaling) {
- if (F[k]->mNumScalingKeys != 0) {
- aiVectorKey key = F[k]->mScalingKeys[0];
- real_t x = key.mValue.x;
- real_t y = key.mValue.y;
- real_t z = key.mValue.z;
- base_scale = Vector3(x, y, z);
- }
- }
- if (is_translation) {
- for (size_t p = 0; p < F[k]->mNumPositionKeys; p++) {
- aiVector3D pos = F[k]->mPositionKeys[p].mValue;
- pos_values.push_back(Vector3(pos.x, pos.y, pos.z));
- pos_times.push_back(F[k]->mPositionKeys[p].mTime / ticks_per_second);
- }
- }
+ ERR_CONTINUE(prop_name.split("*").size() != 2);
- if (is_rotation) {
- for (size_t r = 0; r < F[k]->mNumRotationKeys; r++) {
- aiQuaternion quat = F[k]->mRotationKeys[r].mValue;
- rot_values.push_back(Quat(quat.x, quat.y, quat.z, quat.w).normalized());
- rot_times.push_back(F[k]->mRotationKeys[r].mTime / ticks_per_second);
- }
- }
+ ERR_CONTINUE(!state.node_map.has(mesh_name));
- if (is_scaling) {
- for (size_t sc = 0; sc < F[k]->mNumScalingKeys; sc++) {
- aiVector3D scale = F[k]->mScalingKeys[sc].mValue;
- scale_values.push_back(Vector3(scale.x, scale.y, scale.z));
- scale_times.push_back(F[k]->mScalingKeys[sc].mTime / ticks_per_second);
- }
- }
+ const MeshInstance *mesh_instance = Object::cast_to<MeshInstance>(state.node_map[mesh_name]);
+
+ ERR_CONTINUE(mesh_instance == NULL);
+
+ String base_path = state.root->get_path_to(mesh_instance);
+
+ Ref<Mesh> mesh = mesh_instance->get_mesh();
+ ERR_CONTINUE(mesh.is_null());
+
+ //add the tracks for this mesh
+ int base_track = animation->get_track_count();
+ for (int j = 0; j < mesh->get_blend_shape_count(); j++) {
+
+ animation->add_track(Animation::TYPE_VALUE);
+ animation->track_set_path(base_track + j, base_path + ":blend_shapes/" + mesh->get_blend_shape_name(j));
}
- }
- int32_t track_idx = animation->get_track_count();
- animation->add_track(Animation::TYPE_TRANSFORM);
- animation->track_set_path(track_idx, node_path);
- float increment = 1.0 / float(p_bake_fps);
- float time = 0.0;
- bool last = false;
- while (true) {
- Vector3 pos = Vector3();
- Quat rot = Quat();
- Vector3 scale = Vector3(1.0f, 1.0f, 1.0f);
- if (is_translation && pos_values.size()) {
- pos = _interpolate_track<Vector3>(pos_times, pos_values, time, AssetImportAnimation::INTERP_LINEAR);
- Transform anim_xform;
- String ext = p_path.get_file().get_extension().to_lower();
- if (ext == "fbx") {
- aiNode *ai_node = _ai_find_node(p_scene->mRootNode, p_node_name);
- Transform mesh_xform = _get_global_ai_node_transform(p_scene, ai_node);
- pos = mesh_xform.origin + pos;
- real_t factor = 1.0f;
- if (p_scene->mMetaData != NULL) {
- p_scene->mMetaData->Get("UnitScaleFactor", factor);
- factor = factor * 0.01f;
- }
- pos = pos * factor;
+
+ for (size_t k = 0; k < anim_mesh->mNumKeys; k++) {
+ for (size_t j = 0; j < anim_mesh->mKeys[k].mNumValuesAndWeights; j++) {
+
+ float t = anim_mesh->mKeys[k].mTime / ticks_per_second;
+ float w = anim_mesh->mKeys[k].mWeights[j];
+
+ animation->track_insert_key(base_track + j, t, w);
}
}
- if (is_rotation && rot_values.size()) {
- rot = _interpolate_track<Quat>(rot_times, rot_values, time, AssetImportAnimation::INTERP_LINEAR).normalized();
- }
- if (is_scaling && scale_values.size()) {
- scale = _interpolate_track<Vector3>(scale_times, scale_values, time, AssetImportAnimation::INTERP_LINEAR);
- }
- animation->track_set_interpolation_type(track_idx, Animation::INTERPOLATION_LINEAR);
- animation->transform_track_insert_key(track_idx, time, pos, rot, scale);
+ }
- if (last) {
- break;
- }
- time += increment;
- if (time >= length) {
- last = true;
- time = length;
- }
+ if (animation->get_track_count()) {
+ state.animation_player->add_animation(name, animation);
}
}
@@ -976,611 +741,320 @@ float EditorSceneImporterAssimp::_get_fbx_fps(int32_t time_mode, const aiScene *
return 0;
}
-Transform EditorSceneImporterAssimp::_get_global_ai_node_transform(const aiScene *p_scene, const aiNode *p_current_node) {
+Transform EditorSceneImporterAssimp::_get_global_assimp_node_transform(const aiNode *p_current_node) {
aiNode const *current_node = p_current_node;
Transform xform;
while (current_node != NULL) {
- xform = _ai_matrix_transform(current_node->mTransformation) * xform;
+ xform = _assimp_matrix_transform(current_node->mTransformation) * xform;
current_node = current_node->mParent;
}
return xform;
}
-void EditorSceneImporterAssimp::_generate_node_bone(const aiScene *p_scene, const aiNode *p_node, Map<String, bool> &p_mesh_bones, Skeleton *p_skeleton, const String p_path, const int32_t p_max_bone_weights) {
- for (size_t i = 0; i < p_node->mNumMeshes; i++) {
- const unsigned int mesh_idx = p_node->mMeshes[i];
- const aiMesh *ai_mesh = p_scene->mMeshes[mesh_idx];
- for (size_t j = 0; j < ai_mesh->mNumBones; j++) {
- String bone_name = _ai_string_to_string(ai_mesh->mBones[j]->mName);
- if (p_skeleton->find_bone(bone_name) != -1) {
- continue;
- }
- p_mesh_bones.insert(bone_name, true);
- p_skeleton->add_bone(bone_name);
- int32_t idx = p_skeleton->find_bone(bone_name);
- Transform xform = _ai_matrix_transform(ai_mesh->mBones[j]->mOffsetMatrix);
- String ext = p_path.get_file().get_extension().to_lower();
- if (ext == "fbx") {
- Transform mesh_xform = _get_global_ai_node_transform(p_scene, p_node);
- mesh_xform.basis = Basis();
- xform = mesh_xform.affine_inverse() * xform;
- }
- p_skeleton->set_bone_rest(idx, xform.affine_inverse());
- }
- }
-}
+Ref<Texture> EditorSceneImporterAssimp::_load_texture(ImportState &state, String p_path) {
+ Vector<String> split_path = p_path.get_basename().split("*");
+ if (split_path.size() == 2) {
+ size_t texture_idx = split_path[1].to_int();
+ ERR_FAIL_COND_V(texture_idx >= state.assimp_scene->mNumTextures, Ref<Texture>());
+ aiTexture *tex = state.assimp_scene->mTextures[texture_idx];
+ String filename = _assimp_raw_string_to_string(tex->mFilename);
+ filename = filename.get_file();
+ print_verbose("Open Asset Import: Loading embedded texture " + filename);
+ if (tex->mHeight == 0) {
+ if (tex->CheckFormat("png")) {
+ Ref<Image> img = Image::_png_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth);
+ ERR_FAIL_COND_V(img.is_null(), Ref<Texture>());
-void EditorSceneImporterAssimp::_generate_node_bone_parents(const aiScene *p_scene, const aiNode *p_node, Map<String, bool> &p_mesh_bones, Skeleton *p_skeleton, const MeshInstance *p_mi) {
- for (size_t i = 0; i < p_node->mNumMeshes; i++) {
- const unsigned int mesh_idx = p_node->mMeshes[i];
- const aiMesh *ai_mesh = p_scene->mMeshes[mesh_idx];
-
- for (size_t j = 0; j < ai_mesh->mNumBones; j++) {
- aiNode *bone_node = p_scene->mRootNode->FindNode(ai_mesh->mBones[j]->mName);
- ERR_CONTINUE(bone_node == NULL);
- aiNode *bone_node_parent = bone_node->mParent;
- while (bone_node_parent != NULL) {
- String bone_parent_name = _ai_string_to_string(bone_node_parent->mName);
- bone_parent_name = bone_parent_name.split(ASSIMP_FBX_KEY)[0];
- if (bone_parent_name == p_mi->get_name()) {
- break;
- }
- if (p_mi->get_parent() == NULL) {
- break;
- }
- if (bone_parent_name == p_mi->get_parent()->get_name()) {
- break;
- }
- if (bone_node_parent->mParent == p_scene->mRootNode) {
- break;
- }
- if (p_skeleton->find_bone(bone_parent_name) == -1) {
- p_mesh_bones.insert(bone_parent_name, true);
- }
- bone_node_parent = bone_node_parent->mParent;
- }
- }
- }
-}
-void EditorSceneImporterAssimp::_calculate_skeleton_root(Skeleton *s, const aiScene *p_scene, aiNode *&p_ai_skeleton_root, Map<String, bool> &mesh_bones, const aiNode *p_node) {
- if (s->get_bone_count() > 0) {
- String bone_name = s->get_bone_name(0);
- p_ai_skeleton_root = _ai_find_node(p_scene->mRootNode, bone_name);
- for (size_t i = 0; i < p_scene->mRootNode->mNumChildren; i++) {
- if (p_ai_skeleton_root == NULL) {
- break;
+ Ref<ImageTexture> t;
+ t.instance();
+ t->create_from_image(img);
+ t->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY);
+ return t;
+ } else if (tex->CheckFormat("jpg")) {
+ Ref<Image> img = Image::_jpg_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth);
+ ERR_FAIL_COND_V(img.is_null(), Ref<Texture>());
+ Ref<ImageTexture> t;
+ t.instance();
+ t->create_from_image(img);
+ t->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY);
+ return t;
+ } else if (tex->CheckFormat("dds")) {
+ ERR_EXPLAIN("Open Asset Import: Embedded dds not implemented");
+ ERR_FAIL_COND_V(true, Ref<Texture>());
+ //Ref<Image> img = Image::_dds_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth);
+ //ERR_FAIL_COND_V(img.is_null(), Ref<Texture>());
+ //Ref<ImageTexture> t;
+ //t.instance();
+ //t->create_from_image(img);
+ //t->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY);
+ //return t;
}
- aiNode *found = p_scene->mRootNode->mChildren[i]->FindNode(p_ai_skeleton_root->mName);
- if (found) {
- p_ai_skeleton_root = p_scene->mRootNode->mChildren[i];
- break;
+ } else {
+ Ref<Image> img;
+ img.instance();
+ PoolByteArray arr;
+ uint32_t size = tex->mWidth * tex->mHeight;
+ arr.resize(size);
+ memcpy(arr.write().ptr(), tex->pcData, size);
+ ERR_FAIL_COND_V(arr.size() % 4 != 0, Ref<Texture>());
+ //ARGB8888 to RGBA8888
+ for (int32_t i = 0; i < arr.size() / 4; i++) {
+ arr.write().ptr()[(4 * i) + 3] = arr[(4 * i) + 0];
+ arr.write().ptr()[(4 * i) + 0] = arr[(4 * i) + 1];
+ arr.write().ptr()[(4 * i) + 1] = arr[(4 * i) + 2];
+ arr.write().ptr()[(4 * i) + 2] = arr[(4 * i) + 3];
}
- }
- }
+ img->create(tex->mWidth, tex->mHeight, true, Image::FORMAT_RGBA8, arr);
+ ERR_FAIL_COND_V(img.is_null(), Ref<Texture>());
- if (p_ai_skeleton_root == NULL) {
- p_ai_skeleton_root = p_scene->mRootNode->FindNode(p_node->mName);
- while (p_ai_skeleton_root && p_ai_skeleton_root->mParent && p_ai_skeleton_root->mParent != p_scene->mRootNode) {
- p_ai_skeleton_root = p_scene->mRootNode->FindNode(p_ai_skeleton_root->mName)->mParent;
+ Ref<ImageTexture> t;
+ t.instance();
+ t->create_from_image(img);
+ t->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY);
+ return t;
}
+ return Ref<Texture>();
}
- p_ai_skeleton_root = _ai_find_node(p_scene->mRootNode, _ai_string_to_string(p_ai_skeleton_root->mName).split(ASSIMP_FBX_KEY)[0]);
-}
-
-void EditorSceneImporterAssimp::_fill_skeleton(const aiScene *p_scene, const aiNode *p_node, Spatial *p_current, Node *p_owner, Skeleton *p_skeleton, const Map<String, bool> p_mesh_bones, const Map<String, Transform> &p_bone_rests, Set<String> p_tracks, const String p_path, Set<String> &r_removed_bones) {
- String node_name = _ai_string_to_string(p_node->mName);
- if (p_mesh_bones.find(node_name) != NULL && p_skeleton->find_bone(node_name) == -1) {
- r_removed_bones.insert(node_name);
- p_skeleton->add_bone(node_name);
- int32_t idx = p_skeleton->find_bone(node_name);
- Transform xform = _get_global_ai_node_transform(p_scene, p_node);
- xform = _format_rot_xform(p_path, p_scene) * xform;
- p_skeleton->set_bone_rest(idx, xform);
- }
-
- for (size_t i = 0; i < p_node->mNumChildren; i++) {
- _fill_skeleton(p_scene, p_node->mChildren[i], p_current, p_owner, p_skeleton, p_mesh_bones, p_bone_rests, p_tracks, p_path, r_removed_bones);
- }
+ Ref<Texture> p_texture = ResourceLoader::load(p_path, "Texture");
+ return p_texture;
}
-void EditorSceneImporterAssimp::_keep_node(const String &p_path, Node *p_current, Node *p_owner, Set<Node *> &r_keep_nodes) {
- if (p_current == p_owner) {
- r_keep_nodes.insert(p_current);
- }
-
- if (p_current->get_class() != Spatial().get_class()) {
- r_keep_nodes.insert(p_current);
- }
-
- for (int i = 0; i < p_current->get_child_count(); i++) {
- _keep_node(p_path, p_current->get_child(i), p_owner, r_keep_nodes);
- }
-}
+Ref<Material> EditorSceneImporterAssimp::_generate_material_from_index(ImportState &state, int p_index, bool p_double_sided) {
-void EditorSceneImporterAssimp::_filter_node(const String &p_path, Node *p_current, Node *p_owner, const Set<Node *> p_keep_nodes, Set<String> &r_removed_nodes) {
- if (p_keep_nodes.has(p_current) == false) {
- r_removed_nodes.insert(p_current->get_name());
- p_current->queue_delete();
- }
- for (int i = 0; i < p_current->get_child_count(); i++) {
- _filter_node(p_path, p_current->get_child(i), p_owner, p_keep_nodes, r_removed_nodes);
- }
-}
+ ERR_FAIL_INDEX_V(p_index, (int)state.assimp_scene->mNumMaterials, Ref<Material>());
-void EditorSceneImporterAssimp::_generate_node(const String &p_path, const aiScene *p_scene, const aiNode *p_node, Node *p_parent, Node *p_owner, Set<String> &r_bone_name, Set<String> p_light_names, Set<String> p_camera_names, Map<Skeleton *, MeshInstance *> &r_skeletons, const Map<String, Transform> &p_bone_rests, Vector<MeshInstance *> &r_mesh_instances, int32_t &r_mesh_count, Skeleton *p_skeleton, const int32_t p_max_bone_weights, Set<String> &r_removed_bones, Map<String, Map<uint32_t, String> > &r_name_morph_mesh_names) {
- Spatial *child_node = NULL;
- if (p_node == NULL) {
- return;
- }
- String node_name = _ai_string_to_string(p_node->mName);
- real_t factor = 1.0f;
- String ext = p_path.get_file().get_extension().to_lower();
- if (ext == "fbx") {
- if (p_scene->mMetaData != NULL) {
- p_scene->mMetaData->Get("UnitScaleFactor", factor);
- factor = factor * 0.01f;
- }
- }
- {
- Transform xform = _ai_matrix_transform(p_node->mTransformation);
-
- child_node = memnew(Spatial);
- p_parent->add_child(child_node);
- child_node->set_owner(p_owner);
- if (p_node == p_scene->mRootNode) {
- if ((ext == "fbx") && p_node == p_scene->mRootNode) {
- xform = xform.scaled(Vector3(factor, factor, factor));
- Transform format_xform = _format_rot_xform(p_path, p_scene);
- xform = format_xform * xform;
- }
- }
- child_node->set_transform(xform * child_node->get_transform());
- }
+ aiMaterial *ai_material = state.assimp_scene->mMaterials[p_index];
+ Ref<SpatialMaterial> mat;
+ mat.instance();
- if (p_node->mNumMeshes > 0) {
- MeshInstance *mesh_node = memnew(MeshInstance);
- p_parent->add_child(mesh_node);
- mesh_node->set_owner(p_owner);
- mesh_node->set_transform(child_node->get_transform());
- {
- Map<String, bool> mesh_bones;
- p_skeleton->set_use_bones_in_world_transform(true);
- _generate_node_bone(p_scene, p_node, mesh_bones, p_skeleton, p_path, p_max_bone_weights);
- Set<String> tracks;
- _get_track_set(p_scene, tracks);
- aiNode *skeleton_root = NULL;
- _calculate_skeleton_root(p_skeleton, p_scene, skeleton_root, mesh_bones, p_node);
- _generate_node_bone_parents(p_scene, p_node, mesh_bones, p_skeleton, mesh_node);
- if (p_skeleton->get_bone_count() > 0) {
- _fill_skeleton(p_scene, skeleton_root, mesh_node, p_owner, p_skeleton, mesh_bones, p_bone_rests, tracks, p_path, r_removed_bones);
- _set_bone_parent(p_skeleton, p_owner, p_scene->mRootNode);
- }
- MeshInstance *mi = Object::cast_to<MeshInstance>(mesh_node);
- if (mi) {
- r_mesh_instances.push_back(mi);
- }
- _add_mesh_to_mesh_instance(p_node, p_scene, p_skeleton, p_path, mesh_node, p_owner, r_bone_name, r_mesh_count, p_max_bone_weights, r_name_morph_mesh_names);
- }
- if (mesh_node != NULL && p_skeleton->get_bone_count() > 0 && p_owner->find_node(p_skeleton->get_name()) == NULL) {
- Node *node = p_owner->find_node(_ai_string_to_string(p_scene->mRootNode->mName));
- ERR_FAIL_COND(node == NULL);
- node->add_child(p_skeleton);
- p_skeleton->set_owner(p_owner);
- if (ext == "fbx") {
- Transform mesh_xform = _get_global_ai_node_transform(p_scene, p_node);
- mesh_xform.origin = Vector3();
- p_skeleton->set_transform(mesh_xform);
- }
- r_skeletons.insert(p_skeleton, mesh_node);
- }
- for (size_t i = 0; i < p_node->mNumMeshes; i++) {
- if (p_scene->mMeshes[p_node->mMeshes[i]]->HasBones()) {
- mesh_node->set_name(node_name);
- // Meshes without skeletons must not have skeletons
- mesh_node->set_skeleton_path(String(mesh_node->get_path_to(p_owner)) + "/" + p_owner->get_path_to(p_skeleton));
- }
+ int32_t mat_two_sided = 0;
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_TWOSIDED, mat_two_sided)) {
+ if (mat_two_sided > 0) {
+ mat->set_cull_mode(SpatialMaterial::CULL_DISABLED);
}
- child_node->get_parent()->remove_child(child_node);
- memdelete(child_node);
- child_node = mesh_node;
- } else if (p_light_names.has(node_name)) {
- Spatial *light_node = Object::cast_to<Light>(p_owner->find_node(node_name));
- ERR_FAIL_COND(light_node == NULL);
- if (!p_parent->has_node(light_node->get_path())) {
- p_parent->add_child(light_node);
- }
- light_node->set_owner(p_owner);
- light_node->set_transform(child_node->get_transform().scaled(Vector3(factor, factor, factor)) *
- light_node->get_transform().scaled(Vector3(factor, factor, factor)));
- child_node->get_parent()->remove_child(child_node);
- memdelete(child_node);
- child_node = light_node;
- } else if (p_camera_names.has(node_name)) {
- Spatial *camera_node = Object::cast_to<Camera>(p_owner->find_node(node_name));
- ERR_FAIL_COND(camera_node == NULL);
- if (!p_parent->has_node(camera_node->get_path())) {
- p_parent->add_child(camera_node);
- }
- camera_node->set_owner(p_owner);
- camera_node->set_transform(child_node->get_transform().scaled(Vector3(factor, factor, factor)) *
- camera_node->get_transform().scaled(Vector3(factor, factor, factor)));
- camera_node->scale(Vector3(factor, factor, factor));
- child_node->get_parent()->remove_child(child_node);
- memdelete(child_node);
- child_node = camera_node;
- }
- child_node->set_name(node_name);
- for (size_t i = 0; i < p_node->mNumChildren; i++) {
- _generate_node(p_path, p_scene, p_node->mChildren[i], child_node, p_owner, r_bone_name, p_light_names, p_camera_names, r_skeletons, p_bone_rests, r_mesh_instances, r_mesh_count, p_skeleton, p_max_bone_weights, r_removed_bones, r_name_morph_mesh_names);
}
-}
-
-aiNode *EditorSceneImporterAssimp::_ai_find_node(aiNode *ai_child_node, const String bone_name) {
- if (_ai_string_to_string(ai_child_node->mName) == bone_name) {
- return ai_child_node;
+ //const String mesh_name = _assimp_get_string(ai_mesh->mName);
+ aiString mat_name;
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_NAME, mat_name)) {
+ mat->set_name(_assimp_get_string(mat_name));
}
- aiNode *target = NULL;
- for (size_t i = 0; i < ai_child_node->mNumChildren; i++) {
- target = _ai_find_node(ai_child_node->mChildren[i], bone_name);
- if (target != NULL) {
- return target;
- }
- }
- return target;
-}
+ aiTextureType tex_normal = aiTextureType_NORMALS;
+ {
+ aiString ai_filename = aiString();
+ String filename = "";
+ aiTextureMapMode map_mode[2];
-Transform EditorSceneImporterAssimp::_format_rot_xform(const String p_path, const aiScene *p_scene) {
- String ext = p_path.get_file().get_extension().to_lower();
+ if (AI_SUCCESS == ai_material->GetTexture(tex_normal, 0, &ai_filename, NULL, NULL, NULL, NULL, map_mode)) {
+ filename = _assimp_raw_string_to_string(ai_filename);
+ String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/");
+ bool found = false;
+ _find_texture_path(state.path, path, found);
+ if (found) {
+ Ref<Texture> texture = _load_texture(state, path);
- Transform xform;
- int32_t up_axis = 0;
- Vector3 up_axis_vec3 = Vector3();
-
- int32_t front_axis = 0;
- Vector3 front_axis_vec3 = Vector3();
-
- if (p_scene->mMetaData != NULL) {
- p_scene->mMetaData->Get("UpAxis", up_axis);
- if (up_axis == AssetImportFbx::UP_VECTOR_AXIS_X) {
- if (p_scene->mMetaData != NULL) {
- p_scene->mMetaData->Get("FrontAxis", front_axis);
- if (front_axis == AssetImportFbx::FRONT_PARITY_EVEN) {
- // y
- } else if (front_axis == AssetImportFbx::FRONT_PARITY_ODD) {
- // z
- //front_axis_vec3 = Vector3(0.0f, Math::deg2rad(-180.f), 0.0f);
- }
- }
- } else if (up_axis == AssetImportFbx::UP_VECTOR_AXIS_Y) {
- up_axis_vec3 = Vector3(Math::deg2rad(-90.f), 0.0f, 0.0f);
- if (p_scene->mMetaData != NULL) {
- p_scene->mMetaData->Get("FrontAxis", front_axis);
- if (front_axis == AssetImportFbx::FRONT_PARITY_EVEN) {
- // x
- } else if (front_axis == AssetImportFbx::FRONT_PARITY_ODD) {
- // z
- }
- }
- } else if (up_axis == AssetImportFbx::UP_VECTOR_AXIS_Z) {
- up_axis_vec3 = Vector3(0.0f, Math ::deg2rad(90.f), 0.0f);
- if (p_scene->mMetaData != NULL) {
- p_scene->mMetaData->Get("FrontAxis", front_axis);
- if (front_axis == AssetImportFbx::FRONT_PARITY_EVEN) {
- // x
- } else if (front_axis == AssetImportFbx::FRONT_PARITY_ODD) {
- // y
+ if (texture != NULL) {
+ if (map_mode != NULL) {
+ _set_texture_mapping_mode(map_mode, texture);
+ }
+ mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true);
+ mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture);
}
}
}
}
- int32_t up_axis_sign = 0;
- if (p_scene->mMetaData != NULL) {
- p_scene->mMetaData->Get("UpAxisSign", up_axis_sign);
- up_axis_vec3 = up_axis_vec3 * up_axis_sign;
- }
-
- int32_t front_axis_sign = 0;
- if (p_scene->mMetaData != NULL) {
- p_scene->mMetaData->Get("FrontAxisSign", front_axis_sign);
- front_axis_vec3 = front_axis_vec3 * front_axis_sign;
- }
+ {
+ aiString ai_filename = aiString();
+ String filename = "";
- int32_t coord_axis = 0;
- Vector3 coord_axis_vec3 = Vector3();
- if (p_scene->mMetaData != NULL) {
- p_scene->mMetaData->Get("CoordAxis", coord_axis);
- if (coord_axis == AssetImportFbx::COORD_LEFT) {
- } else if (coord_axis == AssetImportFbx::COORD_RIGHT) {
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_NORMAL_TEXTURE, ai_filename)) {
+ filename = _assimp_raw_string_to_string(ai_filename);
+ String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/");
+ bool found = false;
+ _find_texture_path(state.path, path, found);
+ if (found) {
+ Ref<Texture> texture = _load_texture(state, path);
+ if (texture != NULL) {
+ mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true);
+ mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture);
+ }
+ }
}
}
- int32_t coord_axis_sign = 0;
- if (p_scene->mMetaData != NULL) {
- p_scene->mMetaData->Get("CoordAxisSign", coord_axis_sign);
- }
-
- Quat up_quat;
- up_quat.set_euler(up_axis_vec3);
-
- Quat coord_quat;
- coord_quat.set_euler(coord_axis_vec3);
-
- Quat front_quat;
- front_quat.set_euler(front_axis_vec3);
+ aiTextureType tex_emissive = aiTextureType_EMISSIVE;
- xform.basis.set_quat(up_quat * coord_quat * front_quat);
- return xform;
-}
+ if (ai_material->GetTextureCount(tex_emissive) > 0) {
-void EditorSceneImporterAssimp::_get_track_set(const aiScene *p_scene, Set<String> &tracks) {
- for (size_t i = 0; i < p_scene->mNumAnimations; i++) {
- for (size_t j = 0; j < p_scene->mAnimations[i]->mNumChannels; j++) {
- aiString ai_name = p_scene->mAnimations[i]->mChannels[j]->mNodeName;
- String name = _ai_string_to_string(ai_name);
- tracks.insert(name);
- }
- }
-}
+ aiString ai_filename = aiString();
+ String filename = "";
+ aiTextureMapMode map_mode[2];
-void EditorSceneImporterAssimp::_add_mesh_to_mesh_instance(const aiNode *p_node, const aiScene *p_scene, Skeleton *s, const String &p_path, MeshInstance *p_mesh_instance, Node *p_owner, Set<String> &r_bone_name, int32_t &r_mesh_count, int32_t p_max_bone_weights, Map<String, Map<uint32_t, String> > &r_name_morph_mesh_names) {
- Ref<ArrayMesh> mesh;
- mesh.instance();
- bool has_uvs = false;
- for (size_t i = 0; i < p_node->mNumMeshes; i++) {
- const unsigned int mesh_idx = p_node->mMeshes[i];
- const aiMesh *ai_mesh = p_scene->mMeshes[mesh_idx];
-
- Map<uint32_t, Vector<float> > vertex_weight;
- Map<uint32_t, Vector<String> > vertex_bone_name;
- for (size_t b = 0; b < ai_mesh->mNumBones; b++) {
- aiBone *bone = ai_mesh->mBones[b];
- for (size_t w = 0; w < bone->mNumWeights; w++) {
- String name = _ai_string_to_string(bone->mName);
- aiVertexWeight ai_weights = bone->mWeights[w];
- uint32_t vertexId = ai_weights.mVertexId;
- Map<uint32_t, Vector<float> >::Element *result = vertex_weight.find(vertexId);
- Vector<float> weights;
- if (result != NULL) {
- weights.append_array(result->value());
- }
- weights.push_back(ai_weights.mWeight);
- if (vertex_weight.has(vertexId)) {
- vertex_weight[vertexId] = weights;
- } else {
- vertex_weight.insert(vertexId, weights);
- }
- Map<uint32_t, Vector<String> >::Element *bone_result = vertex_bone_name.find(vertexId);
- Vector<String> bone_names;
- if (bone_result != NULL) {
- bone_names.append_array(bone_result->value());
- }
- bone_names.push_back(name);
- if (vertex_bone_name.has(vertexId)) {
- vertex_bone_name[vertexId] = bone_names;
- } else {
- vertex_bone_name.insert(vertexId, bone_names);
+ if (AI_SUCCESS == ai_material->GetTexture(tex_emissive, 0, &ai_filename, NULL, NULL, NULL, NULL, map_mode)) {
+ filename = _assimp_raw_string_to_string(ai_filename);
+ String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/");
+ bool found = false;
+ _find_texture_path(state.path, path, found);
+ if (found) {
+ Ref<Texture> texture = _load_texture(state, path);
+ if (texture != NULL) {
+ _set_texture_mapping_mode(map_mode, texture);
+ mat->set_feature(SpatialMaterial::FEATURE_EMISSION, true);
+ mat->set_texture(SpatialMaterial::TEXTURE_EMISSION, texture);
}
}
}
+ }
- Ref<SurfaceTool> st;
- st.instance();
- st->begin(Mesh::PRIMITIVE_TRIANGLES);
-
- for (size_t j = 0; j < ai_mesh->mNumVertices; j++) {
- if (ai_mesh->HasTextureCoords(0)) {
- has_uvs = true;
- st->add_uv(Vector2(ai_mesh->mTextureCoords[0][j].x, 1.0f - ai_mesh->mTextureCoords[0][j].y));
- }
- if (ai_mesh->HasTextureCoords(1)) {
- has_uvs = true;
- st->add_uv2(Vector2(ai_mesh->mTextureCoords[1][j].x, 1.0f - ai_mesh->mTextureCoords[1][j].y));
- }
- if (ai_mesh->HasVertexColors(0)) {
- Color color = Color(ai_mesh->mColors[0]->r, ai_mesh->mColors[0]->g, ai_mesh->mColors[0]->b, ai_mesh->mColors[0]->a);
- st->add_color(color);
- }
- if (ai_mesh->mNormals != NULL) {
- const aiVector3D normals = ai_mesh->mNormals[j];
- const Vector3 godot_normal = Vector3(normals.x, normals.y, normals.z);
- st->add_normal(godot_normal);
- if (ai_mesh->HasTangentsAndBitangents()) {
- const aiVector3D tangents = ai_mesh->mTangents[j];
- const Vector3 godot_tangent = Vector3(tangents.x, tangents.y, tangents.z);
- const aiVector3D bitangent = ai_mesh->mBitangents[j];
- const Vector3 godot_bitangent = Vector3(bitangent.x, bitangent.y, bitangent.z);
- float d = godot_normal.cross(godot_tangent).dot(godot_bitangent) > 0.0f ? 1.0f : -1.0f;
- st->add_tangent(Plane(tangents.x, tangents.y, tangents.z, d));
- }
- }
+ aiTextureType tex_albedo = aiTextureType_DIFFUSE;
+ if (ai_material->GetTextureCount(tex_albedo) > 0) {
- if (s != NULL && s->get_bone_count() > 0) {
- Map<uint32_t, Vector<String> >::Element *I = vertex_bone_name.find(j);
- Vector<int32_t> bones;
- if (I != NULL) {
- Vector<String> bone_names;
- bone_names.append_array(I->value());
- for (int32_t f = 0; f < bone_names.size(); f++) {
- int32_t bone = s->find_bone(bone_names[f]);
- ERR_EXPLAIN("Asset Importer: Mesh can't find bone " + bone_names[f]);
- ERR_FAIL_COND(bone == -1);
- bones.push_back(bone);
- }
- if (s->get_bone_count()) {
- int32_t add = CLAMP(p_max_bone_weights - bones.size(), 0, p_max_bone_weights);
- for (int32_t f = 0; f < add; f++) {
- bones.push_back(0);
- }
- }
- st->add_bones(bones);
- Map<uint32_t, Vector<float> >::Element *E = vertex_weight.find(j);
- Vector<float> weights;
- if (E != NULL) {
- weights = E->value();
- if (weights.size() != p_max_bone_weights) {
- int32_t add = CLAMP(p_max_bone_weights - weights.size(), 0, p_max_bone_weights);
- for (int32_t f = 0; f < add; f++) {
- weights.push_back(0.0f);
- }
- }
+ aiString ai_filename = aiString();
+ String filename = "";
+ aiTextureMapMode map_mode[2];
+ if (AI_SUCCESS == ai_material->GetTexture(tex_albedo, 0, &ai_filename, NULL, NULL, NULL, NULL, map_mode)) {
+ filename = _assimp_raw_string_to_string(ai_filename);
+ String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/");
+ bool found = false;
+ _find_texture_path(state.path, path, found);
+ if (found) {
+ Ref<Texture> texture = _load_texture(state, path);
+ if (texture != NULL) {
+ if (texture->get_data()->detect_alpha() != Image::ALPHA_NONE) {
+ _set_texture_mapping_mode(map_mode, texture);
+ mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
+ mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS);
}
- ERR_CONTINUE(weights.size() == 0);
- st->add_weights(weights);
+ mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture);
}
}
- const aiVector3D pos = ai_mesh->mVertices[j];
- Vector3 godot_pos = Vector3(pos.x, pos.y, pos.z);
- st->add_vertex(godot_pos);
}
- for (size_t j = 0; j < ai_mesh->mNumFaces; j++) {
- const aiFace face = ai_mesh->mFaces[j];
- ERR_FAIL_COND(face.mNumIndices != 3);
- Vector<size_t> order;
- order.push_back(2);
- order.push_back(1);
- order.push_back(0);
- for (int32_t k = 0; k < order.size(); k++) {
- st->add_index(face.mIndices[order[k]]);
+ } else {
+ aiColor4D clr_diffuse;
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_COLOR_DIFFUSE, clr_diffuse)) {
+ if (Math::is_equal_approx(clr_diffuse.a, 1.0f) == false) {
+ mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
+ mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS);
+ }
+ mat->set_albedo(Color(clr_diffuse.r, clr_diffuse.g, clr_diffuse.b, clr_diffuse.a));
+ }
+ }
+
+ aiString tex_gltf_base_color_path = aiString();
+ aiTextureMapMode map_mode[2];
+ if (AI_SUCCESS == ai_material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE, &tex_gltf_base_color_path, NULL, NULL, NULL, NULL, map_mode)) {
+ String filename = _assimp_raw_string_to_string(tex_gltf_base_color_path);
+ String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/");
+ bool found = false;
+ _find_texture_path(state.path, path, found);
+ if (found) {
+ Ref<Texture> texture = _load_texture(state, path);
+ _find_texture_path(state.path, path, found);
+ if (texture != NULL) {
+ if (texture->get_data()->detect_alpha() == Image::ALPHA_BLEND) {
+ _set_texture_mapping_mode(map_mode, texture);
+ mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
+ mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS);
+ }
+ mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture);
}
}
- if (ai_mesh->HasTangentsAndBitangents() == false && has_uvs) {
- st->generate_tangents();
- }
- aiMaterial *ai_material = p_scene->mMaterials[ai_mesh->mMaterialIndex];
- Ref<SpatialMaterial> mat;
- mat.instance();
-
- int32_t mat_two_sided = 0;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_TWOSIDED, mat_two_sided)) {
- if (mat_two_sided > 0) {
- mat->set_cull_mode(SpatialMaterial::CULL_DISABLED);
+ } else {
+ aiColor4D pbr_base_color;
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR, pbr_base_color)) {
+ if (Math::is_equal_approx(pbr_base_color.a, 1.0f) == false) {
+ mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
+ mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS);
}
+ mat->set_albedo(Color(pbr_base_color.r, pbr_base_color.g, pbr_base_color.b, pbr_base_color.a));
}
-
- const String mesh_name = _ai_string_to_string(ai_mesh->mName);
- aiString mat_name;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_NAME, mat_name)) {
- mat->set_name(_ai_string_to_string(mat_name));
- }
-
- aiTextureType tex_normal = aiTextureType_NORMALS;
- {
- aiString ai_filename = aiString();
- String filename = "";
- aiTextureMapMode map_mode[2];
-
- if (AI_SUCCESS == ai_material->GetTexture(tex_normal, 0, &ai_filename, NULL, NULL, NULL, NULL, map_mode)) {
- filename = _ai_raw_string_to_string(ai_filename);
- String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/");
- bool found = false;
- _find_texture_path(p_path, path, found);
- if (found) {
- Ref<Texture> texture = _load_texture(p_scene, path);
-
- if (texture != NULL) {
- if (map_mode != NULL) {
- _set_texture_mapping_mode(map_mode, texture);
- }
- mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true);
- mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture);
+ }
+ {
+ aiString tex_fbx_pbs_base_color_path = aiString();
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_BASE_COLOR_TEXTURE, tex_fbx_pbs_base_color_path)) {
+ String filename = _assimp_raw_string_to_string(tex_fbx_pbs_base_color_path);
+ String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/");
+ bool found = false;
+ _find_texture_path(state.path, path, found);
+ if (found) {
+ Ref<Texture> texture = _load_texture(state, path);
+ _find_texture_path(state.path, path, found);
+ if (texture != NULL) {
+ if (texture->get_data()->detect_alpha() == Image::ALPHA_BLEND) {
+ mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
+ mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS);
}
+ mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture);
}
}
- }
-
- {
- aiString ai_filename = aiString();
- String filename = "";
-
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_NORMAL_TEXTURE, ai_filename)) {
- filename = _ai_raw_string_to_string(ai_filename);
- String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/");
- bool found = false;
- _find_texture_path(p_path, path, found);
- if (found) {
- Ref<Texture> texture = _load_texture(p_scene, path);
- if (texture != NULL) {
- mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true);
- mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture);
- }
- }
+ } else {
+ aiColor4D pbr_base_color;
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_BASE_COLOR_FACTOR, pbr_base_color)) {
+ mat->set_albedo(Color(pbr_base_color.r, pbr_base_color.g, pbr_base_color.b, pbr_base_color.a));
}
}
- aiTextureType tex_emissive = aiTextureType_EMISSIVE;
-
- if (ai_material->GetTextureCount(tex_emissive) > 0) {
-
- aiString ai_filename = aiString();
- String filename = "";
- aiTextureMapMode map_mode[2];
+ aiUVTransform pbr_base_color_uv_xform;
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_BASE_COLOR_UV_XFORM, pbr_base_color_uv_xform)) {
+ mat->set_uv1_offset(Vector3(pbr_base_color_uv_xform.mTranslation.x, pbr_base_color_uv_xform.mTranslation.y, 0.0f));
+ mat->set_uv1_scale(Vector3(pbr_base_color_uv_xform.mScaling.x, pbr_base_color_uv_xform.mScaling.y, 1.0f));
+ }
+ }
- if (AI_SUCCESS == ai_material->GetTexture(tex_emissive, 0, &ai_filename, NULL, NULL, NULL, NULL, map_mode)) {
- filename = _ai_raw_string_to_string(ai_filename);
- String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/");
- bool found = false;
- _find_texture_path(p_path, path, found);
- if (found) {
- Ref<Texture> texture = _load_texture(p_scene, path);
- if (texture != NULL) {
- _set_texture_mapping_mode(map_mode, texture);
- mat->set_feature(SpatialMaterial::FEATURE_EMISSION, true);
- mat->set_texture(SpatialMaterial::TEXTURE_EMISSION, texture);
- }
+ {
+ aiString tex_fbx_pbs_normal_path = aiString();
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_NORMAL_TEXTURE, tex_fbx_pbs_normal_path)) {
+ String filename = _assimp_raw_string_to_string(tex_fbx_pbs_normal_path);
+ String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/");
+ bool found = false;
+ _find_texture_path(state.path, path, found);
+ if (found) {
+ Ref<Texture> texture = _load_texture(state, path);
+ _find_texture_path(state.path, path, found);
+ if (texture != NULL) {
+ mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true);
+ mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture);
}
}
}
+ }
- aiTextureType tex_albedo = aiTextureType_DIFFUSE;
- if (ai_material->GetTextureCount(tex_albedo) > 0) {
-
- aiString ai_filename = aiString();
- String filename = "";
- aiTextureMapMode map_mode[2];
- if (AI_SUCCESS == ai_material->GetTexture(tex_albedo, 0, &ai_filename, NULL, NULL, NULL, NULL, map_mode)) {
- filename = _ai_raw_string_to_string(ai_filename);
- String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/");
- bool found = false;
- _find_texture_path(p_path, path, found);
- if (found) {
- Ref<Texture> texture = _load_texture(p_scene, path);
- if (texture != NULL) {
- if (texture->get_data()->detect_alpha() != Image::ALPHA_NONE) {
- _set_texture_mapping_mode(map_mode, texture);
- mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
- mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS);
- }
- mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture);
- }
- }
- }
- } else {
- aiColor4D clr_diffuse;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_COLOR_DIFFUSE, clr_diffuse)) {
- if (Math::is_equal_approx(clr_diffuse.a, 1.0f) == false) {
- mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
- mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS);
+ if (p_double_sided) {
+ mat->set_cull_mode(SpatialMaterial::CULL_DISABLED);
+ }
+
+ {
+ aiString tex_fbx_stingray_normal_path = aiString();
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_NORMAL_TEXTURE, tex_fbx_stingray_normal_path)) {
+ String filename = _assimp_raw_string_to_string(tex_fbx_stingray_normal_path);
+ String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/");
+ bool found = false;
+ _find_texture_path(state.path, path, found);
+ if (found) {
+ Ref<Texture> texture = _load_texture(state, path);
+ _find_texture_path(state.path, path, found);
+ if (texture != NULL) {
+ mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true);
+ mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture);
}
- mat->set_albedo(Color(clr_diffuse.r, clr_diffuse.g, clr_diffuse.b, clr_diffuse.a));
}
}
+ }
- aiString tex_gltf_base_color_path = aiString();
- aiTextureMapMode map_mode[2];
- if (AI_SUCCESS == ai_material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE, &tex_gltf_base_color_path, NULL, NULL, NULL, NULL, map_mode)) {
- String filename = _ai_raw_string_to_string(tex_gltf_base_color_path);
- String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/");
+ {
+ aiString tex_fbx_pbs_base_color_path = aiString();
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_TEXTURE, tex_fbx_pbs_base_color_path)) {
+ String filename = _assimp_raw_string_to_string(tex_fbx_pbs_base_color_path);
+ String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/");
bool found = false;
- _find_texture_path(p_path, path, found);
+ _find_texture_path(state.path, path, found);
if (found) {
- Ref<Texture> texture = _load_texture(p_scene, path);
- _find_texture_path(p_path, path, found);
+ Ref<Texture> texture = _load_texture(state, path);
+ _find_texture_path(state.path, path, found);
if (texture != NULL) {
if (texture->get_data()->detect_alpha() == Image::ALPHA_BLEND) {
- _set_texture_mapping_mode(map_mode, texture);
mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS);
}
@@ -1589,263 +1063,274 @@ void EditorSceneImporterAssimp::_add_mesh_to_mesh_instance(const aiNode *p_node,
}
} else {
aiColor4D pbr_base_color;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR, pbr_base_color)) {
- if (Math::is_equal_approx(pbr_base_color.a, 1.0f) == false) {
- mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
- mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS);
- }
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_BASE_COLOR_FACTOR, pbr_base_color)) {
mat->set_albedo(Color(pbr_base_color.r, pbr_base_color.g, pbr_base_color.b, pbr_base_color.a));
}
}
- {
- aiString tex_fbx_pbs_base_color_path = aiString();
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_BASE_COLOR_TEXTURE, tex_fbx_pbs_base_color_path)) {
- String filename = _ai_raw_string_to_string(tex_fbx_pbs_base_color_path);
- String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/");
- bool found = false;
- _find_texture_path(p_path, path, found);
- if (found) {
- Ref<Texture> texture = _load_texture(p_scene, path);
- _find_texture_path(p_path, path, found);
- if (texture != NULL) {
- if (texture->get_data()->detect_alpha() == Image::ALPHA_BLEND) {
- mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
- mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS);
- }
- mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture);
- }
- }
- } else {
- aiColor4D pbr_base_color;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_BASE_COLOR_FACTOR, pbr_base_color)) {
- mat->set_albedo(Color(pbr_base_color.r, pbr_base_color.g, pbr_base_color.b, pbr_base_color.a));
- }
- }
- aiUVTransform pbr_base_color_uv_xform;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_BASE_COLOR_UV_XFORM, pbr_base_color_uv_xform)) {
- mat->set_uv1_offset(Vector3(pbr_base_color_uv_xform.mTranslation.x, pbr_base_color_uv_xform.mTranslation.y, 0.0f));
- mat->set_uv1_scale(Vector3(pbr_base_color_uv_xform.mScaling.x, pbr_base_color_uv_xform.mScaling.y, 1.0f));
- }
+ aiUVTransform pbr_base_color_uv_xform;
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_UV_XFORM, pbr_base_color_uv_xform)) {
+ mat->set_uv1_offset(Vector3(pbr_base_color_uv_xform.mTranslation.x, pbr_base_color_uv_xform.mTranslation.y, 0.0f));
+ mat->set_uv1_scale(Vector3(pbr_base_color_uv_xform.mScaling.x, pbr_base_color_uv_xform.mScaling.y, 1.0f));
}
+ }
- {
- aiString tex_fbx_pbs_normal_path = aiString();
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_NORMAL_TEXTURE, tex_fbx_pbs_normal_path)) {
- String filename = _ai_raw_string_to_string(tex_fbx_pbs_normal_path);
- String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/");
- bool found = false;
- _find_texture_path(p_path, path, found);
- if (found) {
- Ref<Texture> texture = _load_texture(p_scene, path);
- _find_texture_path(p_path, path, found);
- if (texture != NULL) {
- mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true);
- mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture);
+ {
+ aiString tex_fbx_pbs_emissive_path = aiString();
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_TEXTURE, tex_fbx_pbs_emissive_path)) {
+ String filename = _assimp_raw_string_to_string(tex_fbx_pbs_emissive_path);
+ String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/");
+ bool found = false;
+ _find_texture_path(state.path, path, found);
+ if (found) {
+ Ref<Texture> texture = _load_texture(state, path);
+ _find_texture_path(state.path, path, found);
+ if (texture != NULL) {
+ if (texture->get_data()->detect_alpha() == Image::ALPHA_BLEND) {
+ mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
+ mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS);
}
+ mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture);
}
}
+ } else {
+ aiColor4D pbr_emmissive_color;
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_FACTOR, pbr_emmissive_color)) {
+ mat->set_emission(Color(pbr_emmissive_color.r, pbr_emmissive_color.g, pbr_emmissive_color.b, pbr_emmissive_color.a));
+ }
}
- aiString cull_mode;
- if (p_node->mMetaData) {
- p_node->mMetaData->Get("Culling", cull_mode);
- }
- if (cull_mode.length != 0 && cull_mode == aiString("CullingOff")) {
- mat->set_cull_mode(SpatialMaterial::CULL_DISABLED);
+ real_t pbr_emission_intensity;
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_INTENSITY_FACTOR, pbr_emission_intensity)) {
+ mat->set_emission_energy(pbr_emission_intensity);
}
+ }
- {
- aiString tex_fbx_stingray_normal_path = aiString();
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_NORMAL_TEXTURE, tex_fbx_stingray_normal_path)) {
- String filename = _ai_raw_string_to_string(tex_fbx_stingray_normal_path);
- String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/");
- bool found = false;
- _find_texture_path(p_path, path, found);
- if (found) {
- Ref<Texture> texture = _load_texture(p_scene, path);
- _find_texture_path(p_path, path, found);
- if (texture != NULL) {
- mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true);
- mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture);
- }
- }
+ aiString tex_gltf_pbr_metallicroughness_path;
+ if (AI_SUCCESS == ai_material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE, &tex_gltf_pbr_metallicroughness_path)) {
+ String filename = _assimp_raw_string_to_string(tex_gltf_pbr_metallicroughness_path);
+ String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/");
+ bool found = false;
+ _find_texture_path(state.path, path, found);
+ if (found) {
+ Ref<Texture> texture = _load_texture(state, path);
+ if (texture != NULL) {
+ mat->set_texture(SpatialMaterial::TEXTURE_METALLIC, texture);
+ mat->set_metallic_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_BLUE);
+ mat->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, texture);
+ mat->set_roughness_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GREEN);
}
}
+ } else {
+ float pbr_roughness = 0.0f;
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR, pbr_roughness)) {
+ mat->set_roughness(pbr_roughness);
+ }
+ float pbr_metallic = 0.0f;
- {
- aiString tex_fbx_pbs_base_color_path = aiString();
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_TEXTURE, tex_fbx_pbs_base_color_path)) {
- String filename = _ai_raw_string_to_string(tex_fbx_pbs_base_color_path);
- String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/");
- bool found = false;
- _find_texture_path(p_path, path, found);
- if (found) {
- Ref<Texture> texture = _load_texture(p_scene, path);
- _find_texture_path(p_path, path, found);
- if (texture != NULL) {
- if (texture->get_data()->detect_alpha() == Image::ALPHA_BLEND) {
- mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
- mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS);
- }
- mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture);
- }
- }
- } else {
- aiColor4D pbr_base_color;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_BASE_COLOR_FACTOR, pbr_base_color)) {
- mat->set_albedo(Color(pbr_base_color.r, pbr_base_color.g, pbr_base_color.b, pbr_base_color.a));
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR, pbr_metallic)) {
+ mat->set_metallic(pbr_metallic);
+ }
+ }
+ {
+ aiString tex_fbx_pbs_metallic_path;
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_TEXTURE, tex_fbx_pbs_metallic_path)) {
+ String filename = _assimp_raw_string_to_string(tex_fbx_pbs_metallic_path);
+ String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/");
+ bool found = false;
+ _find_texture_path(state.path, path, found);
+ if (found) {
+ Ref<Texture> texture = _load_texture(state, path);
+ if (texture != NULL) {
+ mat->set_texture(SpatialMaterial::TEXTURE_METALLIC, texture);
+ mat->set_metallic_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GRAYSCALE);
}
}
-
- aiUVTransform pbr_base_color_uv_xform;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_UV_XFORM, pbr_base_color_uv_xform)) {
- mat->set_uv1_offset(Vector3(pbr_base_color_uv_xform.mTranslation.x, pbr_base_color_uv_xform.mTranslation.y, 0.0f));
- mat->set_uv1_scale(Vector3(pbr_base_color_uv_xform.mScaling.x, pbr_base_color_uv_xform.mScaling.y, 1.0f));
+ } else {
+ float pbr_metallic = 0.0f;
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_FACTOR, pbr_metallic)) {
+ mat->set_metallic(pbr_metallic);
}
}
- {
- aiString tex_fbx_pbs_emissive_path = aiString();
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_TEXTURE, tex_fbx_pbs_emissive_path)) {
- String filename = _ai_raw_string_to_string(tex_fbx_pbs_emissive_path);
- String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/");
- bool found = false;
- _find_texture_path(p_path, path, found);
- if (found) {
- Ref<Texture> texture = _load_texture(p_scene, path);
- _find_texture_path(p_path, path, found);
- if (texture != NULL) {
- if (texture->get_data()->detect_alpha() == Image::ALPHA_BLEND) {
- mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
- mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS);
- }
- mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture);
- }
- }
- } else {
- aiColor4D pbr_emmissive_color;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_FACTOR, pbr_emmissive_color)) {
- mat->set_emission(Color(pbr_emmissive_color.r, pbr_emmissive_color.g, pbr_emmissive_color.b, pbr_emmissive_color.a));
+ aiString tex_fbx_pbs_rough_path;
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_TEXTURE, tex_fbx_pbs_rough_path)) {
+ String filename = _assimp_raw_string_to_string(tex_fbx_pbs_rough_path);
+ String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/");
+ bool found = false;
+ _find_texture_path(state.path, path, found);
+ if (found) {
+ Ref<Texture> texture = _load_texture(state, path);
+ if (texture != NULL) {
+ mat->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, texture);
+ mat->set_roughness_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GRAYSCALE);
}
}
+ } else {
+ float pbr_roughness = 0.04f;
- real_t pbr_emission_intensity;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_INTENSITY_FACTOR, pbr_emission_intensity)) {
- mat->set_emission_energy(pbr_emission_intensity);
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_FACTOR, pbr_roughness)) {
+ mat->set_roughness(pbr_roughness);
}
}
+ }
- aiString tex_gltf_pbr_metallicroughness_path;
- if (AI_SUCCESS == ai_material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE, &tex_gltf_pbr_metallicroughness_path)) {
- String filename = _ai_raw_string_to_string(tex_gltf_pbr_metallicroughness_path);
- String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/");
+ {
+ aiString tex_fbx_pbs_metallic_path;
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_METALNESS_TEXTURE, tex_fbx_pbs_metallic_path)) {
+ String filename = _assimp_raw_string_to_string(tex_fbx_pbs_metallic_path);
+ String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/");
bool found = false;
- _find_texture_path(p_path, path, found);
+ _find_texture_path(state.path, path, found);
if (found) {
- Ref<Texture> texture = _load_texture(p_scene, path);
+ Ref<Texture> texture = _load_texture(state, path);
if (texture != NULL) {
mat->set_texture(SpatialMaterial::TEXTURE_METALLIC, texture);
- mat->set_metallic_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_BLUE);
- mat->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, texture);
- mat->set_roughness_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GREEN);
+ mat->set_metallic_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GRAYSCALE);
}
}
} else {
- float pbr_roughness = 0.0f;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR, pbr_roughness)) {
- mat->set_roughness(pbr_roughness);
- }
float pbr_metallic = 0.0f;
-
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR, pbr_metallic)) {
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_METALNESS_FACTOR, pbr_metallic)) {
mat->set_metallic(pbr_metallic);
}
}
- {
- aiString tex_fbx_pbs_metallic_path;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_TEXTURE, tex_fbx_pbs_metallic_path)) {
- String filename = _ai_raw_string_to_string(tex_fbx_pbs_metallic_path);
- String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/");
- bool found = false;
- _find_texture_path(p_path, path, found);
- if (found) {
- Ref<Texture> texture = _load_texture(p_scene, path);
- if (texture != NULL) {
- mat->set_texture(SpatialMaterial::TEXTURE_METALLIC, texture);
- mat->set_metallic_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GRAYSCALE);
- }
- }
- } else {
- float pbr_metallic = 0.0f;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_FACTOR, pbr_metallic)) {
- mat->set_metallic(pbr_metallic);
+
+ aiString tex_fbx_pbs_rough_path;
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_TEXTURE, tex_fbx_pbs_rough_path)) {
+ String filename = _assimp_raw_string_to_string(tex_fbx_pbs_rough_path);
+ String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/");
+ bool found = false;
+ _find_texture_path(state.path, path, found);
+ if (found) {
+ Ref<Texture> texture = _load_texture(state, path);
+ if (texture != NULL) {
+ mat->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, texture);
+ mat->set_roughness_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GRAYSCALE);
}
}
+ } else {
+ float pbr_roughness = 0.04f;
+
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_FACTOR, pbr_roughness)) {
+ mat->set_roughness(pbr_roughness);
+ }
+ }
+ }
+
+ return mat;
+}
+
+Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(ImportState &state, const Vector<int> &p_surface_indices, Skeleton *p_skeleton, bool p_double_sided_material) {
+
+ Ref<ArrayMesh> mesh;
+ mesh.instance();
+ bool has_uvs = false;
- aiString tex_fbx_pbs_rough_path;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_TEXTURE, tex_fbx_pbs_rough_path)) {
- String filename = _ai_raw_string_to_string(tex_fbx_pbs_rough_path);
- String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/");
- bool found = false;
- _find_texture_path(p_path, path, found);
- if (found) {
- Ref<Texture> texture = _load_texture(p_scene, path);
- if (texture != NULL) {
- mat->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, texture);
- mat->set_roughness_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GRAYSCALE);
+ for (int i = 0; i < p_surface_indices.size(); i++) {
+ const unsigned int mesh_idx = p_surface_indices[i];
+ const aiMesh *ai_mesh = state.assimp_scene->mMeshes[mesh_idx];
+
+ Map<uint32_t, Vector<BoneInfo> > vertex_weights;
+
+ if (p_skeleton) {
+ for (size_t b = 0; b < ai_mesh->mNumBones; b++) {
+ aiBone *bone = ai_mesh->mBones[b];
+ String bone_name = _assimp_get_string(bone->mName);
+ int bone_index = p_skeleton->find_bone(bone_name);
+ ERR_CONTINUE(bone_index == -1); //bone refers to an unexisting index, wtf.
+
+ for (size_t w = 0; w < bone->mNumWeights; w++) {
+
+ aiVertexWeight ai_weights = bone->mWeights[w];
+
+ BoneInfo bi;
+
+ uint32_t vertex_index = ai_weights.mVertexId;
+ bi.bone = bone_index;
+ bi.weight = ai_weights.mWeight;
+ ;
+
+ if (!vertex_weights.has(vertex_index)) {
+ vertex_weights[vertex_index] = Vector<BoneInfo>();
}
- }
- } else {
- float pbr_roughness = 0.04f;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_FACTOR, pbr_roughness)) {
- mat->set_roughness(pbr_roughness);
+ vertex_weights[vertex_index].push_back(bi);
}
}
}
- {
- aiString tex_fbx_pbs_metallic_path;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_METALNESS_TEXTURE, tex_fbx_pbs_metallic_path)) {
- String filename = _ai_raw_string_to_string(tex_fbx_pbs_metallic_path);
- String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/");
- bool found = false;
- _find_texture_path(p_path, path, found);
- if (found) {
- Ref<Texture> texture = _load_texture(p_scene, path);
- if (texture != NULL) {
- mat->set_texture(SpatialMaterial::TEXTURE_METALLIC, texture);
- mat->set_metallic_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GRAYSCALE);
- }
- }
- } else {
- float pbr_metallic = 0.0f;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_METALNESS_FACTOR, pbr_metallic)) {
- mat->set_metallic(pbr_metallic);
+ Ref<SurfaceTool> st;
+ st.instance();
+ st->begin(Mesh::PRIMITIVE_TRIANGLES);
+
+ for (size_t j = 0; j < ai_mesh->mNumVertices; j++) {
+ if (ai_mesh->HasTextureCoords(0)) {
+ has_uvs = true;
+ st->add_uv(Vector2(ai_mesh->mTextureCoords[0][j].x, 1.0f - ai_mesh->mTextureCoords[0][j].y));
+ }
+ if (ai_mesh->HasTextureCoords(1)) {
+ has_uvs = true;
+ st->add_uv2(Vector2(ai_mesh->mTextureCoords[1][j].x, 1.0f - ai_mesh->mTextureCoords[1][j].y));
+ }
+ if (ai_mesh->HasVertexColors(0)) {
+ Color color = Color(ai_mesh->mColors[0]->r, ai_mesh->mColors[0]->g, ai_mesh->mColors[0]->b, ai_mesh->mColors[0]->a);
+ st->add_color(color);
+ }
+ if (ai_mesh->mNormals != NULL) {
+ const aiVector3D normals = ai_mesh->mNormals[j];
+ const Vector3 godot_normal = Vector3(normals.x, normals.y, normals.z);
+ st->add_normal(godot_normal);
+ if (ai_mesh->HasTangentsAndBitangents()) {
+ const aiVector3D tangents = ai_mesh->mTangents[j];
+ const Vector3 godot_tangent = Vector3(tangents.x, tangents.y, tangents.z);
+ const aiVector3D bitangent = ai_mesh->mBitangents[j];
+ const Vector3 godot_bitangent = Vector3(bitangent.x, bitangent.y, bitangent.z);
+ float d = godot_normal.cross(godot_tangent).dot(godot_bitangent) > 0.0f ? 1.0f : -1.0f;
+ st->add_tangent(Plane(tangents.x, tangents.y, tangents.z, d));
}
}
- aiString tex_fbx_pbs_rough_path;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_TEXTURE, tex_fbx_pbs_rough_path)) {
- String filename = _ai_raw_string_to_string(tex_fbx_pbs_rough_path);
- String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/");
- bool found = false;
- _find_texture_path(p_path, path, found);
- if (found) {
- Ref<Texture> texture = _load_texture(p_scene, path);
- if (texture != NULL) {
- mat->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, texture);
- mat->set_roughness_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GRAYSCALE);
- }
- }
- } else {
- float pbr_roughness = 0.04f;
+ if (vertex_weights.has(j)) {
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_FACTOR, pbr_roughness)) {
- mat->set_roughness(pbr_roughness);
+ Vector<BoneInfo> bone_info = vertex_weights[j];
+ Vector<int> bones;
+ bones.resize(bone_info.size());
+ Vector<float> weights;
+ weights.resize(bone_info.size());
+ for (int k = 0; k < bone_info.size(); k++) {
+ bones.write[k] = bone_info[k].bone;
+ weights.write[k] = bone_info[k].weight;
}
+
+ st->add_bones(bones);
+ st->add_weights(weights);
}
+
+ const aiVector3D pos = ai_mesh->mVertices[j];
+ Vector3 godot_pos = Vector3(pos.x, pos.y, pos.z);
+ st->add_vertex(godot_pos);
+ }
+
+ for (size_t j = 0; j < ai_mesh->mNumFaces; j++) {
+ const aiFace face = ai_mesh->mFaces[j];
+ ERR_CONTINUE(face.mNumIndices != 3);
+ Vector<size_t> order;
+ order.push_back(2);
+ order.push_back(1);
+ order.push_back(0);
+ for (int32_t k = 0; k < order.size(); k++) {
+ st->add_index(face.mIndices[order[k]]);
+ }
+ }
+ if (ai_mesh->HasTangentsAndBitangents() == false && has_uvs) {
+ st->generate_tangents();
+ }
+
+ Ref<Material> material;
+
+ if (!state.material_cache.has(ai_mesh->mMaterialIndex)) {
+ material = _generate_material_from_index(state, ai_mesh->mMaterialIndex, p_double_sided_material);
}
Array array_mesh = st->commit_to_arrays();
@@ -1855,13 +1340,16 @@ void EditorSceneImporterAssimp::_add_mesh_to_mesh_instance(const aiNode *p_node,
Map<uint32_t, String> morph_mesh_idx_names;
for (size_t j = 0; j < ai_mesh->mNumAnimMeshes; j++) {
- String ai_anim_mesh_name = _ai_string_to_string(ai_mesh->mAnimMeshes[j]->mName);
- mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_NORMALIZED);
- if (ai_anim_mesh_name.empty()) {
- ai_anim_mesh_name = String("morph_") + itos(j);
+ if (i == 0) {
+ //only do this the first time
+ String ai_anim_mesh_name = _assimp_get_string(ai_mesh->mAnimMeshes[j]->mName);
+ mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_NORMALIZED);
+ if (ai_anim_mesh_name.empty()) {
+ ai_anim_mesh_name = String("morph_") + itos(j);
+ }
+ mesh->add_blend_shape(ai_anim_mesh_name);
}
- mesh->add_blend_shape(ai_anim_mesh_name);
- morph_mesh_idx_names.insert(j, ai_anim_mesh_name);
+
Array array_copy;
array_copy.resize(VisualServer::ARRAY_MAX);
@@ -1945,82 +1433,192 @@ void EditorSceneImporterAssimp::_add_mesh_to_mesh_instance(const aiNode *p_node,
morphs[j] = array_copy;
}
- r_name_morph_mesh_names.insert(_ai_string_to_string(p_node->mName), morph_mesh_idx_names);
+
mesh->add_surface_from_arrays(primitive, array_mesh, morphs);
- mesh->surface_set_material(i, mat);
- mesh->surface_set_name(i, _ai_string_to_string(ai_mesh->mName));
- r_mesh_count++;
- print_line(String("Open Asset Import: Created mesh (including instances) ") + _ai_string_to_string(ai_mesh->mName) + " " + itos(r_mesh_count) + " of " + itos(p_scene->mNumMeshes));
+ mesh->surface_set_material(i, material);
+ mesh->surface_set_name(i, _assimp_get_string(ai_mesh->mName));
}
- p_mesh_instance->set_mesh(mesh);
+
+ return mesh;
}
-Ref<Texture> EditorSceneImporterAssimp::_load_texture(const aiScene *p_scene, String p_path) {
- Vector<String> split_path = p_path.get_basename().split("*");
- if (split_path.size() == 2) {
- size_t texture_idx = split_path[1].to_int();
- ERR_FAIL_COND_V(texture_idx >= p_scene->mNumTextures, Ref<Texture>());
- aiTexture *tex = p_scene->mTextures[texture_idx];
- String filename = _ai_raw_string_to_string(tex->mFilename);
- filename = filename.get_file();
- print_verbose("Open Asset Import: Loading embedded texture " + filename);
- if (tex->mHeight == 0) {
- if (tex->CheckFormat("png")) {
- Ref<Image> img = Image::_png_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth);
- ERR_FAIL_COND_V(img.is_null(), Ref<Texture>());
+void EditorSceneImporterAssimp::_generate_node(ImportState &state, const aiNode *p_assimp_node, Node *p_parent) {
- Ref<ImageTexture> t;
- t.instance();
- t->create_from_image(img);
- t->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY);
- return t;
- } else if (tex->CheckFormat("jpg")) {
- Ref<Image> img = Image::_jpg_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth);
- ERR_FAIL_COND_V(img.is_null(), Ref<Texture>());
- Ref<ImageTexture> t;
- t.instance();
- t->create_from_image(img);
- t->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY);
- return t;
- } else if (tex->CheckFormat("dds")) {
- ERR_EXPLAIN("Open Asset Import: Embedded dds not implemented");
- ERR_FAIL_COND_V(true, Ref<Texture>());
- //Ref<Image> img = Image::_dds_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth);
- //ERR_FAIL_COND_V(img.is_null(), Ref<Texture>());
- //Ref<ImageTexture> t;
- //t.instance();
- //t->create_from_image(img);
- //t->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY);
- //return t;
+ Spatial *new_node = NULL;
+ String node_name = _assimp_get_string(p_assimp_node->mName);
+ Transform node_transform = _assimp_matrix_transform(p_assimp_node->mTransformation);
+
+ if (p_assimp_node->mNumMeshes > 0) {
+ /* MESH NODE */
+ Ref<Mesh> mesh;
+ Skeleton *skeleton = NULL;
+ {
+
+ //see if we have mesh cache for this.
+ Vector<int> surface_indices;
+ for (uint32_t i = 0; i < p_assimp_node->mNumMeshes; i++) {
+ int mesh_index = p_assimp_node->mMeshes[i];
+ surface_indices.push_back(mesh_index);
+
+ //take the chane and attempt to find the skeleton from the bones
+ if (!skeleton) {
+ aiMesh *ai_mesh = state.assimp_scene->mMeshes[p_assimp_node->mMeshes[i]];
+ for (uint32_t j = 0; j < ai_mesh->mNumBones; j++) {
+ aiBone *bone = ai_mesh->mBones[j];
+ String bone_name = _assimp_get_string(bone->mName);
+ if (state.bone_owners.has(bone_name)) {
+ skeleton = state.skeletons[state.bone_owners[bone_name]];
+ break;
+ }
+ }
+ }
}
- } else {
- Ref<Image> img;
- img.instance();
- PoolByteArray arr;
- uint32_t size = tex->mWidth * tex->mHeight;
- arr.resize(size);
- memcpy(arr.write().ptr(), tex->pcData, size);
- ERR_FAIL_COND_V(arr.size() % 4 != 0, Ref<Texture>());
- //ARGB8888 to RGBA8888
- for (int32_t i = 0; i < arr.size() / 4; i++) {
- arr.write().ptr()[(4 * i) + 3] = arr[(4 * i) + 0];
- arr.write().ptr()[(4 * i) + 0] = arr[(4 * i) + 1];
- arr.write().ptr()[(4 * i) + 1] = arr[(4 * i) + 2];
- arr.write().ptr()[(4 * i) + 2] = arr[(4 * i) + 3];
+ surface_indices.sort();
+ String mesh_key;
+ for (int i = 0; i < surface_indices.size(); i++) {
+ if (i > 0) {
+ mesh_key += ":";
+ }
+ mesh_key += itos(surface_indices[i]);
}
- img->create(tex->mWidth, tex->mHeight, true, Image::FORMAT_RGBA8, arr);
- ERR_FAIL_COND_V(img.is_null(), Ref<Texture>());
- Ref<ImageTexture> t;
- t.instance();
- t->create_from_image(img);
- t->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY);
- return t;
+ if (!state.mesh_cache.has(mesh_key)) {
+ //adding cache
+ aiString cull_mode; //cull is on mesh, which is kind of stupid tbh
+ bool double_sided_material = false;
+ if (p_assimp_node->mMetaData) {
+ p_assimp_node->mMetaData->Get("Culling", cull_mode);
+ }
+ if (cull_mode.length != 0 && cull_mode == aiString("CullingOff")) {
+ double_sided_material = true;
+ }
+
+ mesh = _generate_mesh_from_surface_indices(state, surface_indices, skeleton, double_sided_material);
+ state.mesh_cache[mesh_key] = mesh;
+ }
+
+ mesh = state.mesh_cache[mesh_key];
}
- return Ref<Texture>();
+
+ MeshInstance *mesh_node = memnew(MeshInstance);
+ if (skeleton) {
+ state.mesh_skeletons[mesh_node] = skeleton;
+ }
+ mesh_node->set_mesh(mesh);
+ new_node = mesh_node;
+
+ } else if (state.light_cache.has(node_name)) {
+
+ Light *light = NULL;
+ aiLight *ai_light = state.assimp_scene->mLights[state.light_cache[node_name]];
+ ERR_FAIL_COND(!ai_light);
+
+ if (ai_light->mType == aiLightSource_DIRECTIONAL) {
+ light = memnew(DirectionalLight);
+ Vector3 dir = Vector3(ai_light->mDirection.y, ai_light->mDirection.x, ai_light->mDirection.z);
+ dir.normalize();
+ Vector3 pos = Vector3(ai_light->mPosition.x, ai_light->mPosition.y, ai_light->mPosition.z);
+ Vector3 up = Vector3(ai_light->mUp.x, ai_light->mUp.y, ai_light->mUp.z);
+ up.normalize();
+
+ Transform light_transform;
+ light_transform.set_look_at(pos, pos + dir, up);
+
+ node_transform *= light_transform;
+
+ } else if (ai_light->mType == aiLightSource_POINT) {
+ light = memnew(OmniLight);
+ Vector3 pos = Vector3(ai_light->mPosition.x, ai_light->mPosition.y, ai_light->mPosition.z);
+ Transform xform;
+ xform.origin = pos;
+
+ node_transform *= xform;
+
+ light->set_transform(xform);
+
+ //light->set_param(Light::PARAM_ATTENUATION, 1);
+ } else if (ai_light->mType == aiLightSource_SPOT) {
+ light = memnew(SpotLight);
+
+ Vector3 dir = Vector3(ai_light->mDirection.y, ai_light->mDirection.x, ai_light->mDirection.z);
+ dir.normalize();
+ Vector3 pos = Vector3(ai_light->mPosition.x, ai_light->mPosition.y, ai_light->mPosition.z);
+ Vector3 up = Vector3(ai_light->mUp.x, ai_light->mUp.y, ai_light->mUp.z);
+ up.normalize();
+
+ Transform light_transform;
+ light_transform.set_look_at(pos, pos + dir, up);
+ node_transform *= light_transform;
+
+ //light->set_param(Light::PARAM_ATTENUATION, 0.0f);
+ }
+ ERR_FAIL_COND(light == NULL);
+ light->set_color(Color(ai_light->mColorDiffuse.r, ai_light->mColorDiffuse.g, ai_light->mColorDiffuse.b));
+ new_node = light;
+ } else if (state.camera_cache.has(node_name)) {
+
+ aiCamera *ai_camera = state.assimp_scene->mCameras[state.camera_cache[node_name]];
+ ERR_FAIL_COND(!ai_camera);
+
+ Camera *camera = memnew(Camera);
+
+ float near = ai_camera->mClipPlaneNear;
+ if (Math::is_equal_approx(near, 0.0f)) {
+ near = 0.1f;
+ }
+ camera->set_perspective(Math::rad2deg(ai_camera->mHorizontalFOV) * 2.0f, near, ai_camera->mClipPlaneFar);
+
+ Vector3 pos = Vector3(ai_camera->mPosition.x, ai_camera->mPosition.y, ai_camera->mPosition.z);
+ Vector3 look_at = Vector3(ai_camera->mLookAt.y, ai_camera->mLookAt.x, ai_camera->mLookAt.z).normalized();
+ Vector3 up = Vector3(ai_camera->mUp.x, ai_camera->mUp.y, ai_camera->mUp.z);
+
+ Transform xform;
+ xform.set_look_at(pos, look_at, up);
+
+ new_node = camera;
+ } else if (state.bone_owners.has(node_name)) {
+
+ //have to actually put the skeleton somewhere, you know.
+ Skeleton *skeleton = state.skeletons[state.bone_owners[node_name]];
+ if (skeleton->get_parent()) {
+ //a bone for a skeleton already added..
+ //could go downwards here to add meshes children of skeleton bones
+ //but let's not support it for now.
+ return;
+ }
+ //restore rest poses to local, now that we know where the skeleton finally is
+ Transform skeleton_transform;
+ if (p_assimp_node->mParent) {
+ skeleton_transform = _get_global_assimp_node_transform(p_assimp_node->mParent);
+ }
+ for (int i = 0; i < skeleton->get_bone_count(); i++) {
+ Transform rest = skeleton_transform.affine_inverse() * skeleton->get_bone_rest(i);
+ skeleton->set_bone_rest(i, rest.affine_inverse());
+ }
+
+ skeleton->localize_rests();
+ node_name = "Skeleton"; //don't use the bone root name
+ node_transform = Transform(); //dont transform
+
+ new_node = skeleton;
+ } else {
+ //generic node
+ new_node = memnew(Spatial);
+ }
+
+ {
+
+ new_node->set_name(node_name);
+ new_node->set_transform(node_transform);
+ p_parent->add_child(new_node);
+ new_node->set_owner(state.root);
+ }
+
+ state.node_map[node_name] = new_node;
+
+ for (size_t i = 0; i < p_assimp_node->mNumChildren; i++) {
+ _generate_node(state, p_assimp_node->mChildren[i], new_node);
}
- Ref<Texture> p_texture = ResourceLoader::load(p_path, "Texture");
- return p_texture;
}
void EditorSceneImporterAssimp::_calc_tangent_from_mesh(const aiMesh *ai_mesh, int i, int tri_index, int index, PoolColorArray::Write &w) {
@@ -2122,31 +1720,25 @@ void EditorSceneImporterAssimp::_find_texture_path(const String &p_path, _Direct
}
}
-String EditorSceneImporterAssimp::_ai_string_to_string(const aiString p_string) const {
- Vector<char> raw_name;
- raw_name.resize(p_string.length);
- memcpy(raw_name.ptrw(), p_string.C_Str(), p_string.length);
+String EditorSceneImporterAssimp::_assimp_get_string(const aiString p_string) const {
+ //convert an assimp String to a Godot String
String name;
- name.parse_utf8(raw_name.ptrw(), raw_name.size());
+ name.parse_utf8(p_string.C_Str() /*,p_string.length*/);
if (name.find(":") != -1) {
String replaced_name = name.split(":")[1];
print_verbose("Replacing " + name + " containing : with " + replaced_name);
name = replaced_name;
}
- if (name.find(".") != -1) {
- String replaced_name = name.replace(".", "");
- print_verbose("Replacing " + name + " containing . with " + replaced_name);
- name = replaced_name;
- }
+
+ name = name.replace(".", ""); //can break things, specially bone names
+
return name;
}
-String EditorSceneImporterAssimp::_ai_anim_string_to_string(const aiString p_string) const {
- Vector<char> raw_name;
- raw_name.resize(p_string.length);
- memcpy(raw_name.ptrw(), p_string.C_Str(), p_string.length);
+String EditorSceneImporterAssimp::_assimp_anim_string_to_string(const aiString p_string) const {
+
String name;
- name.parse_utf8(raw_name.ptrw(), raw_name.size());
+ name.parse_utf8(p_string.C_Str() /*,p_string.length*/);
if (name.find(":") != -1) {
String replaced_name = name.split(":")[1];
print_verbose("Replacing " + name + " containing : with " + replaced_name);
@@ -2155,12 +1747,9 @@ String EditorSceneImporterAssimp::_ai_anim_string_to_string(const aiString p_str
return name;
}
-String EditorSceneImporterAssimp::_ai_raw_string_to_string(const aiString p_string) const {
- Vector<char> raw_name;
- raw_name.resize(p_string.length);
- memcpy(raw_name.ptrw(), p_string.C_Str(), p_string.length);
+String EditorSceneImporterAssimp::_assimp_raw_string_to_string(const aiString p_string) const {
String name;
- name.parse_utf8(raw_name.ptrw(), raw_name.size());
+ name.parse_utf8(p_string.C_Str() /*,p_string.length*/);
return name;
}
@@ -2168,14 +1757,10 @@ Ref<Animation> EditorSceneImporterAssimp::import_animation(const String &p_path,
return Ref<Animation>();
}
-const Transform EditorSceneImporterAssimp::_ai_matrix_transform(const aiMatrix4x4 p_matrix) {
+const Transform EditorSceneImporterAssimp::_assimp_matrix_transform(const aiMatrix4x4 p_matrix) {
aiMatrix4x4 matrix = p_matrix;
Transform xform;
- xform.set(matrix.a1, matrix.b1, matrix.c1, matrix.a2, matrix.b2, matrix.c2, matrix.a3, matrix.b3, matrix.c3, matrix.a4, matrix.b4, matrix.c4);
- xform.basis.inverse();
- xform.basis.transpose();
- Vector3 scale = xform.basis.get_scale();
- Quat rot = xform.basis.get_rotation_quat();
- xform.basis.set_quat_scale(rot, scale);
+ //xform.set(matrix.a1, matrix.b1, matrix.c1, matrix.a2, matrix.b2, matrix.c2, matrix.a3, matrix.b3, matrix.c3, matrix.a4, matrix.b4, matrix.c4);
+ xform.set(matrix.a1, matrix.a2, matrix.a3, matrix.b1, matrix.b2, matrix.b3, matrix.c1, matrix.c2, matrix.c3, matrix.a4, matrix.b4, matrix.c4);
return xform;
}
diff --git a/modules/assimp/editor_scene_importer_assimp.h b/modules/assimp/editor_scene_importer_assimp.h
index 8f9ed434ae..598845236e 100644
--- a/modules/assimp/editor_scene_importer_assimp.h
+++ b/modules/assimp/editor_scene_importer_assimp.h
@@ -146,37 +146,65 @@ private:
COORD_LEFT = 1
};
};
- Spatial *_generate_scene(const String &p_path, const aiScene *scene, const uint32_t p_flags, int p_bake_fps, const int32_t p_max_bone_weights);
- void _fill_kept_node(Set<Node *> &keep_nodes);
- String _find_skeleton_bone_root(Map<Skeleton *, MeshInstance *> &skeletons, Map<MeshInstance *, String> &meshes, Spatial *root);
- void _set_bone_parent(Skeleton *s, Node *p_owner, aiNode *p_node);
- Transform _get_global_ai_node_transform(const aiScene *p_scene, const aiNode *p_current_node);
- void _generate_node_bone(const aiScene *p_scene, const aiNode *p_node, Map<String, bool> &p_mesh_bones, Skeleton *p_skeleton, const String p_path, const int32_t p_max_bone_weights);
- void _generate_node_bone_parents(const aiScene *p_scene, const aiNode *p_node, Map<String, bool> &p_mesh_bones, Skeleton *p_skeleton, const MeshInstance *p_mi);
- void _calculate_skeleton_root(Skeleton *s, const aiScene *p_scene, aiNode *&p_ai_skeleton_root, Map<String, bool> &mesh_bones, const aiNode *p_node);
- void _fill_skeleton(const aiScene *p_scene, const aiNode *p_node, Spatial *p_current, Node *p_owner, Skeleton *p_skeleton, const Map<String, bool> p_mesh_bones, const Map<String, Transform> &p_bone_rests, Set<String> p_tracks, const String p_path, Set<String> &r_removed_bones);
- void _keep_node(const String &p_path, Node *p_current, Node *p_owner, Set<Node *> &r_keep_nodes);
- void _filter_node(const String &p_path, Node *p_current, Node *p_owner, const Set<Node *> p_keep_nodes, Set<String> &r_removed_nodes);
- void _generate_node(const String &p_path, const aiScene *p_scene, const aiNode *p_node, Node *p_parent, Node *p_owner, Set<String> &r_bone_name, Set<String> p_light_names, Set<String> p_camera_names, Map<Skeleton *, MeshInstance *> &r_skeletons, const Map<String, Transform> &p_bone_rests, Vector<MeshInstance *> &r_mesh_instances, int32_t &r_mesh_count, Skeleton *p_skeleton, const int32_t p_max_bone_weights, Set<String> &r_removed_bones, Map<String, Map<uint32_t, String> > &r_name_morph_mesh_names);
- aiNode *_ai_find_node(aiNode *ai_child_node, const String bone_name);
- Transform _format_rot_xform(const String p_path, const aiScene *p_scene);
- void _get_track_set(const aiScene *p_scene, Set<String> &tracks);
- void _insert_animation_track(const aiScene *p_scene, const String p_path, int p_bake_fps, Ref<Animation> animation, float ticks_per_second, float length, const Skeleton *sk, const aiNodeAnim *track, String node_name, NodePath node_path);
- void _add_mesh_to_mesh_instance(const aiNode *p_node, const aiScene *p_scene, Skeleton *s, const String &p_path, MeshInstance *p_mesh_instance, Node *p_owner, Set<String> &r_bone_name, int32_t &r_mesh_count, int32_t p_max_bone_weights, Map<String, Map<uint32_t, String> > &r_name_morph_mesh_names);
- Ref<Texture> _load_texture(const aiScene *p_scene, String p_path);
+
+ struct ImportState {
+
+ String path;
+ const aiScene *assimp_scene;
+ uint32_t max_bone_weights;
+ Spatial *root;
+ Map<String, Ref<Mesh> > mesh_cache;
+ Map<int, Ref<Material> > material_cache;
+ Map<String, int> light_cache;
+ Map<String, int> camera_cache;
+ Vector<Skeleton *> skeletons;
+ Map<String, int> bone_owners; //maps bones to skeleton index owned by
+ Map<String, Node *> node_map;
+ Map<MeshInstance *, Skeleton *> mesh_skeletons;
+ bool fbx; //for some reason assimp does some things different for FBX
+ AnimationPlayer *animation_player;
+ };
+
+ struct BoneInfo {
+ uint32_t bone;
+ float weight;
+ };
+
+ struct SkeletonHole { //nodes may be part of the skeleton by used by vertex
+ String name;
+ String parent;
+ Transform pose;
+ const aiNode *node;
+ };
+
+ const Transform _assimp_matrix_transform(const aiMatrix4x4 p_matrix);
+ String _assimp_get_string(const aiString p_string) const;
+ Transform _get_global_assimp_node_transform(const aiNode *p_current_node);
+
void _calc_tangent_from_mesh(const aiMesh *ai_mesh, int i, int tri_index, int index, PoolColorArray::Write &w);
void _set_texture_mapping_mode(aiTextureMapMode *map_mode, Ref<Texture> texture);
void _find_texture_path(const String &p_path, String &path, bool &r_found);
void _find_texture_path(const String &p_path, _Directory &dir, String &path, bool &found, String extension);
- String _ai_string_to_string(const aiString p_string) const;
- String _ai_anim_string_to_string(const aiString p_string) const;
- String _ai_raw_string_to_string(const aiString p_string) const;
- void _import_animation(const String p_path, const Vector<MeshInstance *> p_meshes, const aiScene *p_scene, AnimationPlayer *ap, int32_t p_index, int p_bake_fps, Map<Skeleton *, MeshInstance *> p_skeletons, const Set<String> p_removed_nodes, const Set<String> removed_bones, const Map<String, Map<uint32_t, String> > p_path_morph_mesh_names);
- void _insert_pivot_anim_track(const Vector<MeshInstance *> p_meshes, const String p_node_name, Vector<const aiNodeAnim *> F, AnimationPlayer *ap, Skeleton *sk, float &length, float ticks_per_second, Ref<Animation> animation, int p_bake_fps, const String &p_path, const aiScene *p_scene);
+
+ Ref<Texture> _load_texture(ImportState &state, String p_path);
+ Ref<Material> _generate_material_from_index(ImportState &state, int p_index, bool p_double_sided);
+ Ref<Mesh> _generate_mesh_from_surface_indices(ImportState &state, const Vector<int> &p_surface_indices, Skeleton *p_skeleton = NULL, bool p_double_sided_material = false);
+ void _generate_node(ImportState &state, const aiNode *p_assimp_node, Node *p_parent);
+ void _generate_bone_groups(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<String, Transform> &bind_xforms);
+ void _fill_node_relationships(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<int, int> &skeleton_map, int p_skeleton_id, Skeleton *p_skeleton, const String &p_parent_name, int &holecount, const Vector<SkeletonHole> &p_holes, const Map<String, Transform> &bind_xforms);
+ void _generate_skeletons(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<int, int> &skeleton_map, const Map<String, Transform> &bind_xforms);
+
+ void _insert_animation_track(ImportState &scene, const aiAnimation *assimp_anim, int p_track, int p_bake_fps, Ref<Animation> animation, float ticks_per_second, Skeleton *p_skeleton, const NodePath &p_path, const String &p_name);
+
+ void _import_animation(ImportState &state, int p_animation_index, int p_bake_fps);
+
+ Spatial *_generate_scene(const String &p_path, const aiScene *scene, const uint32_t p_flags, int p_bake_fps, const int32_t p_max_bone_weights);
+
+ String _assimp_anim_string_to_string(const aiString p_string) const;
+ String _assimp_raw_string_to_string(const aiString p_string) const;
float _get_fbx_fps(int32_t time_mode, const aiScene *p_scene);
template <class T>
T _interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time, AssetImportAnimation::Interpolation p_interp);
- const Transform _ai_matrix_transform(const aiMatrix4x4 p_matrix);
void _register_project_setting_import(const String generic, const String import_setting_string, const Vector<String> &exts, List<String> *r_extensions, const bool p_enabled) const;
struct ImportFormat {
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 5f97069485..3c9644127c 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -1424,6 +1424,34 @@ void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const {
for (Map<StringName, PropertyInfo>::Element *E = script->member_info.front(); E; E = E->next()) {
p_properties->push_back(E->value());
}
+
+ // Call _get_property_list
+
+ ERR_FAIL_COND(!script.is_valid());
+
+ MonoObject *mono_object = get_mono_object();
+ ERR_FAIL_NULL(mono_object);
+
+ GDMonoClass *top = script->script_class;
+
+ while (top && top != script->native) {
+ GDMonoMethod *method = top->get_method(CACHED_STRING_NAME(_get_property_list), 0);
+
+ if (method) {
+ MonoObject *ret = method->invoke(mono_object);
+
+ if (ret) {
+ Array array = Array(GDMonoMarshal::mono_object_to_variant(ret));
+ for (int i = 0, size = array.size(); i < size; i++)
+ p_properties->push_back(PropertyInfo::from_dict(array.get(i)));
+ return;
+ }
+
+ break;
+ }
+
+ top = top->get_parent_class();
+ }
}
Variant::Type CSharpInstance::get_property_type(const StringName &p_name, bool *r_is_valid) const {
@@ -3027,6 +3055,7 @@ CSharpLanguage::StringNameCache::StringNameCache() {
_signal_callback = StaticCString::create("_signal_callback");
_set = StaticCString::create("_set");
_get = StaticCString::create("_get");
+ _get_property_list = StaticCString::create("_get_property_list");
_notification = StaticCString::create("_notification");
_script_source = StaticCString::create("script/source");
dotctor = StaticCString::create(".ctor");
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index 99877a4379..050527d52b 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -298,6 +298,7 @@ class CSharpLanguage : public ScriptLanguage {
StringName _signal_callback;
StringName _set;
StringName _get;
+ StringName _get_property_list;
StringName _notification;
StringName _script_source;
StringName dotctor; // .ctor
diff --git a/modules/opensimplex/doc_classes/OpenSimplexNoise.xml b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml
index b5bc35df69..06d22a2d65 100644
--- a/modules/opensimplex/doc_classes/OpenSimplexNoise.xml
+++ b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml
@@ -37,6 +37,14 @@
Generate a noise image with the requested [code]width[/code] and [code]height[/code], based on the current noise parameters.
</description>
</method>
+ <method name="get_noise_1d">
+ <return type="float">
+ </return>
+ <argument index="0" name="x" type="float">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="get_noise_2d">
<return type="float">
</return>
diff --git a/scene/3d/skeleton.cpp b/scene/3d/skeleton.cpp
index b7279e4d4f..dafcf95b1f 100644
--- a/scene/3d/skeleton.cpp
+++ b/scene/3d/skeleton.cpp
@@ -540,10 +540,11 @@ void Skeleton::clear_bones() {
void Skeleton::set_bone_pose(int p_bone, const Transform &p_pose) {
ERR_FAIL_INDEX(p_bone, bones.size());
- ERR_FAIL_COND(!is_inside_tree());
bones.write[p_bone].pose = p_pose;
- _make_dirty();
+ if (is_inside_tree()) {
+ _make_dirty();
+ }
}
Transform Skeleton::get_bone_pose(int p_bone) const {
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 92f7d6b588..7e59606d2b 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -2850,7 +2850,7 @@ void Tree::_notification(int p_what) {
if (p_what == NOTIFICATION_DRAG_BEGIN) {
single_select_defer = NULL;
- if (cache.scroll_speed > 0 && get_rect().has_point(get_viewport()->get_mouse_position() - get_global_position())) {
+ if (cache.scroll_speed > 0) {
scrolling = true;
set_physics_process_internal(true);
}
@@ -2897,22 +2897,22 @@ void Tree::_notification(int p_what) {
}
}
- if (scrolling) {
- Point2 point = get_viewport()->get_mouse_position() - get_global_position();
- if (point.x < cache.scroll_border) {
- point.x -= cache.scroll_border;
- } else if (point.x > get_size().width - cache.scroll_border) {
- point.x -= get_size().width - cache.scroll_border;
- } else {
- point.x = 0;
+ Point2 mouse_position = get_viewport()->get_mouse_position() - get_global_position();
+ if (scrolling && get_rect().grow(cache.scroll_border).has_point(mouse_position)) {
+ Point2 point;
+
+ if ((ABS(mouse_position.x) < ABS(mouse_position.x - get_size().width)) && (ABS(mouse_position.x) < cache.scroll_border)) {
+ point.x = mouse_position.x - cache.scroll_border;
+ } else if (ABS(mouse_position.x - get_size().width) < cache.scroll_border) {
+ point.x = mouse_position.x - (get_size().width - cache.scroll_border);
}
- if (point.y < cache.scroll_border) {
- point.y -= cache.scroll_border;
- } else if (point.y > get_size().height - cache.scroll_border) {
- point.y -= get_size().height - cache.scroll_border;
- } else {
- point.y = 0;
+
+ if ((ABS(mouse_position.y) < ABS(mouse_position.y - get_size().height)) && (ABS(mouse_position.y) < cache.scroll_border)) {
+ point.y = mouse_position.y - cache.scroll_border;
+ } else if (ABS(mouse_position.y - get_size().height) < cache.scroll_border) {
+ point.y = mouse_position.y - (get_size().height - cache.scroll_border);
}
+
point *= cache.scroll_speed * get_physics_process_delta_time();
point += get_scroll();
h_scroll->set_value(point.x);
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 87a15bbeda..3148df15dd 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -521,6 +521,8 @@ void register_scene_types() {
ClassDB::register_class<VisualShaderNodeTransformUniform>();
ClassDB::register_class<VisualShaderNodeTextureUniform>();
ClassDB::register_class<VisualShaderNodeCubeMapUniform>();
+ ClassDB::register_class<VisualShaderNodeIf>();
+ ClassDB::register_class<VisualShaderNodeSwitch>();
ClassDB::register_class<ShaderMaterial>();
ClassDB::register_virtual_class<CanvasItem>();
diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp
index c0abee2030..3ba43006a3 100644
--- a/scene/resources/surface_tool.cpp
+++ b/scene/resources/surface_tool.cpp
@@ -108,8 +108,55 @@ void SurfaceTool::add_vertex(const Vector3 &p_vertex) {
vtx.bones = last_bones;
vtx.tangent = last_tangent.normal;
vtx.binormal = last_normal.cross(last_tangent.normal).normalized() * last_tangent.d;
+
+ const int expected_vertices = 4;
+
+ if ((format & Mesh::ARRAY_FORMAT_WEIGHTS || format & Mesh::ARRAY_FORMAT_BONES) && (vtx.weights.size() != expected_vertices || vtx.bones.size() != expected_vertices)) {
+ //ensure vertices are the expected amount
+ ERR_FAIL_COND(vtx.weights.size() != vtx.bones.size());
+ if (vtx.weights.size() < expected_vertices) {
+ //less than requred, fill
+ for (int i = vtx.weights.size(); i < expected_vertices; i++) {
+ vtx.weights.push_back(0);
+ vtx.bones.push_back(0);
+ }
+ } else if (vtx.weights.size() > expected_vertices) {
+ //more than required, sort, cap and normalize.
+ Vector<WeightSort> weights;
+ for (int i = 0; i < vtx.weights.size(); i++) {
+ WeightSort ws;
+ ws.index = vtx.bones[i];
+ ws.weight = vtx.weights[i];
+ weights.push_back(ws);
+ }
+
+ //sort
+ weights.sort();
+ //cap
+ weights.resize(expected_vertices);
+ //renormalize
+ float total = 0;
+ for (int i = 0; i < expected_vertices; i++) {
+ total += weights[i].weight;
+ }
+
+ vtx.weights.resize(expected_vertices);
+ vtx.bones.resize(expected_vertices);
+
+ for (int i = 0; i < expected_vertices; i++) {
+ if (total > 0) {
+ vtx.weights.write[i] = weights[i].weight / total;
+ } else {
+ vtx.weights.write[i] = 0;
+ }
+ vtx.bones.write[i] = weights[i].index;
+ }
+ }
+ }
+
vertex_array.push_back(vtx);
first = false;
+
format |= Mesh::ARRAY_FORMAT_VERTEX;
}
void SurfaceTool::add_color(Color p_color) {
@@ -161,7 +208,6 @@ void SurfaceTool::add_uv2(const Vector2 &p_uv2) {
void SurfaceTool::add_bones(const Vector<int> &p_bones) {
ERR_FAIL_COND(!begun);
- ERR_FAIL_COND(p_bones.size() != 4);
ERR_FAIL_COND(!first && !(format & Mesh::ARRAY_FORMAT_BONES));
format |= Mesh::ARRAY_FORMAT_BONES;
@@ -171,8 +217,6 @@ void SurfaceTool::add_bones(const Vector<int> &p_bones) {
void SurfaceTool::add_weights(const Vector<float> &p_weights) {
ERR_FAIL_COND(!begun);
-
- ERR_FAIL_COND(p_weights.size() != 4);
ERR_FAIL_COND(!first && !(format & Mesh::ARRAY_FORMAT_WEIGHTS));
format |= Mesh::ARRAY_FORMAT_WEIGHTS;
diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h
index a3b110f0d8..c55cade813 100644
--- a/scene/resources/surface_tool.h
+++ b/scene/resources/surface_tool.h
@@ -62,6 +62,14 @@ private:
static _FORCE_INLINE_ uint32_t hash(const Vertex &p_vtx);
};
+ struct WeightSort {
+ int index;
+ float weight;
+ bool operator<(const WeightSort &p_right) const {
+ return weight < p_right.weight;
+ }
+ };
+
bool begun;
bool first;
Mesh::PrimitiveType primitive;
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index fdb6f2ce09..4229147ba2 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -782,7 +782,7 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
} else if (in_type == VisualShaderNode::PORT_TYPE_VECTOR && out_type == VisualShaderNode::PORT_TYPE_SCALAR) {
inputs[i] = "vec3(" + src_var + ")";
} else if (in_type == VisualShaderNode::PORT_TYPE_BOOLEAN && out_type == VisualShaderNode::PORT_TYPE_VECTOR) {
- inputs[i] = "all(" + src_var + ")";
+ inputs[i] = "all(bvec3(" + src_var + "))";
} else if (in_type == VisualShaderNode::PORT_TYPE_BOOLEAN && out_type == VisualShaderNode::PORT_TYPE_SCALAR) {
inputs[i] = src_var + ">0.0?true:false";
} else if (in_type == VisualShaderNode::PORT_TYPE_SCALAR && out_type == VisualShaderNode::PORT_TYPE_BOOLEAN) {
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index dac999bf1d..d02902572c 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -2948,3 +2948,140 @@ String VisualShaderNodeCubeMapUniform::generate_code(Shader::Mode p_mode, Visual
VisualShaderNodeCubeMapUniform::VisualShaderNodeCubeMapUniform() {
}
+
+////////////// If
+
+String VisualShaderNodeIf::get_caption() const {
+ return "If";
+}
+
+int VisualShaderNodeIf::get_input_port_count() const {
+ return 6;
+}
+
+VisualShaderNodeIf::PortType VisualShaderNodeIf::get_input_port_type(int p_port) const {
+ if (p_port == 0 || p_port == 1 || p_port == 2) {
+ return PORT_TYPE_SCALAR;
+ }
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeIf::get_input_port_name(int p_port) const {
+ switch (p_port) {
+ case 0:
+ return "a";
+ case 1:
+ return "b";
+ case 2:
+ return "tolerance";
+ case 3:
+ return "a == b";
+ case 4:
+ return "a > b";
+ case 5:
+ return "a < b";
+ default:
+ return "";
+ }
+}
+
+int VisualShaderNodeIf::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeIf::PortType VisualShaderNodeIf::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeIf::get_output_port_name(int p_port) const {
+ return "result";
+}
+
+String VisualShaderNodeIf::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+
+ String code;
+ code += "\tif(abs(" + p_input_vars[0] + "-" + p_input_vars[1] + ")<" + p_input_vars[2] + ")\n"; // abs(a - b) < tolerance eg. a == b
+ code += "\t{\n";
+ code += "\t\t" + p_output_vars[0] + "=" + p_input_vars[3] + ";\n";
+ code += "\t}\n";
+ code += "\telse if(" + p_input_vars[0] + "<" + p_input_vars[1] + ")\n"; // a < b
+ code += "\t{\n";
+ code += "\t\t" + p_output_vars[0] + "=" + p_input_vars[5] + ";\n";
+ code += "\t}\n";
+ code += "\telse\n"; // a > b (or a >= b if abs(a - b) < tolerance is false)
+ code += "\t{\n";
+ code += "\t\t" + p_output_vars[0] + "=" + p_input_vars[4] + ";\n";
+ code += "\t}\n";
+ return code;
+}
+
+VisualShaderNodeIf::VisualShaderNodeIf() {
+ set_input_port_default_value(0, 0.0);
+ set_input_port_default_value(1, 0.0);
+ set_input_port_default_value(2, CMP_EPSILON);
+ set_input_port_default_value(3, Vector3(0.0, 0.0, 0.0));
+ set_input_port_default_value(4, Vector3(0.0, 0.0, 0.0));
+ set_input_port_default_value(5, Vector3(0.0, 0.0, 0.0));
+}
+
+////////////// Switch
+
+String VisualShaderNodeSwitch::get_caption() const {
+ return "Switch";
+}
+
+int VisualShaderNodeSwitch::get_input_port_count() const {
+ return 3;
+}
+
+VisualShaderNodeSwitch::PortType VisualShaderNodeSwitch::get_input_port_type(int p_port) const {
+ if (p_port == 0) {
+ return PORT_TYPE_BOOLEAN;
+ }
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeSwitch::get_input_port_name(int p_port) const {
+ switch (p_port) {
+ case 0:
+ return "value";
+ case 1:
+ return "true";
+ case 2:
+ return "false";
+ default:
+ return "";
+ }
+}
+
+int VisualShaderNodeSwitch::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeSwitch::PortType VisualShaderNodeSwitch::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR;
+}
+
+String VisualShaderNodeSwitch::get_output_port_name(int p_port) const {
+ return "result";
+}
+
+String VisualShaderNodeSwitch::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+
+ String code;
+ code += "\tif(" + p_input_vars[0] + ")\n";
+ code += "\t{\n";
+ code += "\t\t" + p_output_vars[0] + "=" + p_input_vars[1] + ";\n";
+ code += "\t}\n";
+ code += "\telse\n";
+ code += "\t{\n";
+ code += "\t\t" + p_output_vars[0] + "=" + p_input_vars[2] + ";\n";
+ code += "\t}\n";
+ return code;
+}
+
+VisualShaderNodeSwitch::VisualShaderNodeSwitch() {
+ set_input_port_default_value(0, false);
+ set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0));
+ set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0));
+}
diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h
index 28ead64fe2..90c479bd48 100644
--- a/scene/resources/visual_shader_nodes.h
+++ b/scene/resources/visual_shader_nodes.h
@@ -1440,4 +1440,48 @@ public:
VisualShaderNodeCubeMapUniform();
};
+///////////////////////////////////////
+/// IF
+///////////////////////////////////////
+
+class VisualShaderNodeIf : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeIf, VisualShaderNode)
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const;
+
+ VisualShaderNodeIf();
+};
+
+///////////////////////////////////////
+/// SWITCH
+///////////////////////////////////////
+
+class VisualShaderNodeSwitch : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeSwitch, VisualShaderNode)
+public:
+ virtual String get_caption() const;
+
+ virtual int get_input_port_count() const;
+ virtual PortType get_input_port_type(int p_port) const;
+ virtual String get_input_port_name(int p_port) const;
+
+ virtual int get_output_port_count() const;
+ virtual PortType get_output_port_type(int p_port) const;
+ virtual String get_output_port_name(int p_port) const;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const;
+
+ VisualShaderNodeSwitch();
+};
+
#endif // VISUAL_SHADER_NODES_H